mirror of https://github.com/dirtbags/moth.git
work on getting mkpuzzle to work
This commit is contained in:
parent
7c22520b70
commit
7b06171839
|
@ -0,0 +1,64 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/afero"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BasePathFs is an overloaded afero.BasePathFs that has a recursive RealPath().
|
||||||
|
type BasePathFs struct {
|
||||||
|
afero.Fs
|
||||||
|
source afero.Fs
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBasePathFs returns a new BasePathFs.
|
||||||
|
func NewBasePathFs(source afero.Fs, path string) afero.Fs {
|
||||||
|
return &BasePathFs{
|
||||||
|
Fs: afero.NewBasePathFs(source, path),
|
||||||
|
source: source,
|
||||||
|
path: path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RealPath returns the real path to a file, "breaking out" of the BasePathFs.
|
||||||
|
func (b *BasePathFs) RealPath(name string) (path string, err error) {
|
||||||
|
if err := validateBasePathName(name); err != nil {
|
||||||
|
return name, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bpath := filepath.Clean(b.path)
|
||||||
|
path = filepath.Clean(filepath.Join(bpath, name))
|
||||||
|
|
||||||
|
if parentBasePathFs, ok := b.source.(*BasePathFs); ok {
|
||||||
|
return parentBasePathFs.RealPath(path)
|
||||||
|
} else if parentBasePathFs, ok := b.source.(*afero.BasePathFs); ok {
|
||||||
|
return parentBasePathFs.RealPath(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(path, bpath) {
|
||||||
|
return name, os.ErrNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateBasePathName(name string) error {
|
||||||
|
if runtime.GOOS != "windows" {
|
||||||
|
// Not much to do here;
|
||||||
|
// the virtual file paths all look absolute on *nix.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// On Windows a common mistake would be to provide an absolute OS path
|
||||||
|
// We could strip out the base part, but that would not be very portable.
|
||||||
|
if filepath.IsAbs(name) {
|
||||||
|
return os.ErrNotExist
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -10,7 +10,7 @@ import (
|
||||||
// NewCategory returns a new category for the given path in the given fs.
|
// NewCategory returns a new category for the given path in the given fs.
|
||||||
func NewCategory(fs afero.Fs, cat string) Category {
|
func NewCategory(fs afero.Fs, cat string) Category {
|
||||||
return Category{
|
return Category{
|
||||||
Fs: afero.NewBasePathFs(fs, cat),
|
Fs: NewBasePathFs(fs, cat),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ func (c Category) Puzzles() ([]int, error) {
|
||||||
return puzzles, nil
|
return puzzles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Puzzle returns the Puzzle associated with points.
|
// PuzzleDir returns the PuzzleDir associated with points.
|
||||||
func (c Category) Puzzle(points int) (*Puzzle, error) {
|
func (c Category) PuzzleDir(points int) *PuzzleDir {
|
||||||
return NewPuzzle(c.Fs, points)
|
return NewPuzzleDir(c.Fs, points)
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,20 +86,21 @@ func (t *T) PrintInventory() error {
|
||||||
// Open writes a file to the writer.
|
// Open writes a file to the writer.
|
||||||
func (t *T) Open() error {
|
func (t *T) Open() error {
|
||||||
c := t.NewCategory(t.Cat)
|
c := t.NewCategory(t.Cat)
|
||||||
p, err := c.Puzzle(t.Points)
|
pd := c.PuzzleDir(t.Points)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch t.Filename {
|
switch t.Filename {
|
||||||
case "puzzle.json", "":
|
case "puzzle.json", "":
|
||||||
|
p, err := pd.Export()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
jp, err := json.Marshal(p)
|
jp, err := json.Marshal(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
t.w.Write(jp)
|
t.w.Write(jp)
|
||||||
default:
|
default:
|
||||||
f, err := p.Open(t.Filename)
|
f, err := pd.Open(t.Filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,10 +47,11 @@ body
|
||||||
afero.WriteFile(fs, "cat0/21/puzzle.md", []byte("Answer: broken\nSpooon\n"), 0644)
|
afero.WriteFile(fs, "cat0/21/puzzle.md", []byte("Answer: broken\nSpooon\n"), 0644)
|
||||||
afero.WriteFile(fs, "cat0/22/puzzle.md", []byte("---\nanswers:\n - pencil\npre:\n body: Spooon\n---\nSpoon?\n"), 0644)
|
afero.WriteFile(fs, "cat0/22/puzzle.md", []byte("---\nanswers:\n - pencil\npre:\n body: Spooon\n---\nSpoon?\n"), 0644)
|
||||||
afero.WriteFile(fs, "cat1/93/puzzle.md", []byte("Answer: no\n\nbody"), 0644)
|
afero.WriteFile(fs, "cat1/93/puzzle.md", []byte("Answer: no\n\nbody"), 0644)
|
||||||
|
afero.WriteFile(fs, "cat1/barney/puzzle.md", testMothYaml, 0644)
|
||||||
return fs
|
return fs
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestThings(t *testing.T) {
|
func TestEverything(t *testing.T) {
|
||||||
stdout := new(bytes.Buffer)
|
stdout := new(bytes.Buffer)
|
||||||
tp := T{
|
tp := T{
|
||||||
w: stdout,
|
w: stdout,
|
||||||
|
@ -75,8 +76,8 @@ func TestThings(t *testing.T) {
|
||||||
if err := json.Unmarshal(stdout.Bytes(), &p); err != nil {
|
if err := json.Unmarshal(stdout.Bytes(), &p); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
if p.Answers[0] != "YAML answer" {
|
if (len(p.Answers) != 1) || (p.Answers[0] != "YAML answer") {
|
||||||
t.Error("Didn't return the right object")
|
t.Error("Didn't return the right object", p)
|
||||||
}
|
}
|
||||||
|
|
||||||
stdout.Reset()
|
stdout.Reset()
|
||||||
|
|
|
@ -7,7 +7,9 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -18,18 +20,159 @@ import (
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewPuzzle returns a new Puzzle for points.
|
// NewPuzzleDir returns a new PuzzleDir for points.
|
||||||
func NewPuzzle(fs afero.Fs, points int) (*Puzzle, error) {
|
func NewPuzzleDir(fs afero.Fs, points int) *PuzzleDir {
|
||||||
p := &Puzzle{
|
pd := &PuzzleDir{
|
||||||
fs: afero.NewBasePathFs(fs, strconv.Itoa(points)),
|
fs: NewBasePathFs(fs, strconv.Itoa(points)),
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.parseMoth(); err != nil {
|
|
||||||
return p, err
|
|
||||||
}
|
}
|
||||||
// BUG(neale): Doesn't yet handle "puzzle.py" or "mkpuzzle"
|
// BUG(neale): Doesn't yet handle "puzzle.py" or "mkpuzzle"
|
||||||
|
|
||||||
|
return pd
|
||||||
|
}
|
||||||
|
|
||||||
|
// PuzzleDir is a single puzzle's directory.
|
||||||
|
type PuzzleDir struct {
|
||||||
|
fs afero.Fs
|
||||||
|
mkpuzzle bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open returns a newly-opened file.
|
||||||
|
func (pd *PuzzleDir) Open(name string) (io.ReadCloser, error) {
|
||||||
|
// BUG(neale): You cannot open generated files in puzzles, only files actually on the disk
|
||||||
|
if _, err := pd.fs.Stat(""
|
||||||
|
return pd.fs.Open(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export returns a Puzzle struct for the current puzzle.
|
||||||
|
func (pd *PuzzleDir) Export() (Puzzle, error) {
|
||||||
|
p, staticErr := pd.exportStatic()
|
||||||
|
if staticErr == nil {
|
||||||
return p, nil
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only fall through if the static files don't exist. Otherwise, report the error.
|
||||||
|
if !os.IsNotExist(staticErr) {
|
||||||
|
return p, staticErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if p, cmdErr := pd.exportCommand(); cmdErr == nil {
|
||||||
|
return p, nil
|
||||||
|
} else if os.IsNotExist(cmdErr) {
|
||||||
|
// If the command doesn't exist either, report the non-existence of the static file instead.
|
||||||
|
return p, staticErr
|
||||||
|
} else {
|
||||||
|
return p, cmdErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pd *PuzzleDir) exportStatic() (Puzzle, error) {
|
||||||
|
r, err := pd.fs.Open("puzzle.md")
|
||||||
|
if err != nil {
|
||||||
|
var err2 error
|
||||||
|
if r, err2 = pd.fs.Open("puzzle.moth"); err2 != nil {
|
||||||
|
return Puzzle{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
headerBuf := new(bytes.Buffer)
|
||||||
|
headerParser := rfc822HeaderParser
|
||||||
|
headerEnd := ""
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
lineNo := 0
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
lineNo++
|
||||||
|
if lineNo == 1 {
|
||||||
|
if line == "---" {
|
||||||
|
headerParser = yamlHeaderParser
|
||||||
|
headerEnd = "---"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if line == headerEnd {
|
||||||
|
headerBuf.WriteRune('\n')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
headerBuf.WriteString(line)
|
||||||
|
headerBuf.WriteRune('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyBuf := new(bytes.Buffer)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
lineNo++
|
||||||
|
bodyBuf.WriteString(line)
|
||||||
|
bodyBuf.WriteRune('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
puzzle, err := headerParser(headerBuf)
|
||||||
|
if err != nil {
|
||||||
|
return puzzle, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Markdownify the body
|
||||||
|
if puzzle.Pre.Body != "" {
|
||||||
|
if bodyBuf.Len() > 0 {
|
||||||
|
return puzzle, fmt.Errorf("Puzzle body present in header and in moth body")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
puzzle.Pre.Body = string(blackfriday.Run(bodyBuf.Bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return puzzle, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pd *PuzzleDir) exportCommand() (Puzzle, error) {
|
||||||
|
bfs, ok := pd.fs.(*BasePathFs)
|
||||||
|
if !ok {
|
||||||
|
return Puzzle{}, fmt.Errorf("Fs won't resolve real paths for %v", pd)
|
||||||
|
}
|
||||||
|
mkpuzzlePath, err := bfs.RealPath("mkpuzzle")
|
||||||
|
if err != nil {
|
||||||
|
return Puzzle{}, err
|
||||||
|
}
|
||||||
|
log.Print(mkpuzzlePath)
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
cmd := exec.CommandContext(ctx, mkpuzzlePath)
|
||||||
|
stdout, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return Puzzle{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
jsdec := json.NewDecoder(bytes.NewReader(stdout))
|
||||||
|
jsdec.DisallowUnknownFields()
|
||||||
|
puzzle := Puzzle{}
|
||||||
|
if err := jsdec.Decode(&puzzle); err != nil {
|
||||||
|
return Puzzle{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return puzzle, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func legacyAttachmentParser(val []string) []Attachment {
|
||||||
|
ret := make([]Attachment, len(val))
|
||||||
|
for idx, txt := range val {
|
||||||
|
parts := strings.SplitN(txt, " ", 3)
|
||||||
|
cur := Attachment{}
|
||||||
|
cur.FilesystemPath = parts[0]
|
||||||
|
if len(parts) > 1 {
|
||||||
|
cur.Filename = parts[1]
|
||||||
|
} else {
|
||||||
|
cur.Filename = cur.FilesystemPath
|
||||||
|
}
|
||||||
|
if (len(parts) > 2) && (parts[2] == "hidden") {
|
||||||
|
cur.Listed = false
|
||||||
|
} else {
|
||||||
|
cur.Listed = true
|
||||||
|
}
|
||||||
|
ret[idx] = cur
|
||||||
|
}
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// Puzzle contains everything about a puzzle.
|
// Puzzle contains everything about a puzzle.
|
||||||
|
@ -56,7 +199,6 @@ type Puzzle struct {
|
||||||
Summary string
|
Summary string
|
||||||
}
|
}
|
||||||
Answers []string
|
Answers []string
|
||||||
fs afero.Fs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attachment carries information about an attached file.
|
// Attachment carries information about an attached file.
|
||||||
|
@ -66,37 +208,19 @@ type Attachment struct {
|
||||||
Listed bool // Whether this file is listed as an attachment
|
Listed bool // Whether this file is listed as an attachment
|
||||||
}
|
}
|
||||||
|
|
||||||
func legacyAttachmentParser(val []string) []Attachment {
|
func yamlHeaderParser(r io.Reader) (Puzzle, error) {
|
||||||
ret := make([]Attachment, len(val))
|
p := Puzzle{}
|
||||||
for idx, txt := range val {
|
|
||||||
parts := strings.SplitN(txt, " ", 3)
|
|
||||||
cur := Attachment{}
|
|
||||||
cur.FilesystemPath = parts[0]
|
|
||||||
if len(parts) > 1 {
|
|
||||||
cur.Filename = parts[1]
|
|
||||||
} else {
|
|
||||||
cur.Filename = cur.FilesystemPath
|
|
||||||
}
|
|
||||||
if (len(parts) > 2) && (parts[2] == "hidden") {
|
|
||||||
cur.Listed = false
|
|
||||||
} else {
|
|
||||||
cur.Listed = true
|
|
||||||
}
|
|
||||||
ret[idx] = cur
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Puzzle) yamlHeaderParser(r io.Reader) error {
|
|
||||||
decoder := yaml.NewDecoder(r)
|
decoder := yaml.NewDecoder(r)
|
||||||
decoder.SetStrict(true)
|
decoder.SetStrict(true)
|
||||||
return decoder.Decode(p)
|
err := decoder.Decode(&p)
|
||||||
|
return p, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Puzzle) rfc822HeaderParser(r io.Reader) error {
|
func rfc822HeaderParser(r io.Reader) (Puzzle, error) {
|
||||||
|
p := Puzzle{}
|
||||||
m, err := mail.ReadMessage(r)
|
m, err := mail.ReadMessage(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Parsing RFC822 headers: %v", err)
|
return p, fmt.Errorf("Parsing RFC822 headers: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, val := range m.Header {
|
for key, val := range m.Header {
|
||||||
|
@ -119,99 +243,9 @@ func (p *Puzzle) rfc822HeaderParser(r io.Reader) error {
|
||||||
case "ksa":
|
case "ksa":
|
||||||
p.Post.KSAs = val
|
p.Post.KSAs = val
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("Unknown header field: %s", key)
|
return p, fmt.Errorf("Unknown header field: %s", key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return p, nil
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Puzzle) parseMoth() error {
|
|
||||||
r, err := p.fs.Open("puzzle.md")
|
|
||||||
if err != nil {
|
|
||||||
var err2 error
|
|
||||||
if r, err2 = p.fs.Open("puzzle.moth"); err2 != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defer r.Close()
|
|
||||||
|
|
||||||
headerBuf := new(bytes.Buffer)
|
|
||||||
headerParser := p.rfc822HeaderParser
|
|
||||||
headerEnd := ""
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(r)
|
|
||||||
lineNo := 0
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
lineNo++
|
|
||||||
if lineNo == 1 {
|
|
||||||
if line == "---" {
|
|
||||||
headerParser = p.yamlHeaderParser
|
|
||||||
headerEnd = "---"
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if line == headerEnd {
|
|
||||||
headerBuf.WriteRune('\n')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
headerBuf.WriteString(line)
|
|
||||||
headerBuf.WriteRune('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
bodyBuf := new(bytes.Buffer)
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
lineNo++
|
|
||||||
bodyBuf.WriteString(line)
|
|
||||||
bodyBuf.WriteRune('\n')
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := headerParser(headerBuf); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Markdownify the body
|
|
||||||
if (p.Pre.Body != "") && (bodyBuf.Len() > 0) {
|
|
||||||
return fmt.Errorf("Puzzle body present in header and in moth body")
|
|
||||||
}
|
|
||||||
p.Pre.Body = string(blackfriday.Run(bodyBuf.Bytes()))
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Puzzle) mkpuzzle() error {
|
|
||||||
bfs, ok := p.fs.(*afero.BasePathFs)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("Fs won't resolve real paths for %v", p)
|
|
||||||
}
|
|
||||||
mkpuzzlePath, err := bfs.RealPath("mkpuzzle")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
cmd := exec.CommandContext(ctx, mkpuzzlePath)
|
|
||||||
stdout, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
jsdec := json.NewDecoder(bytes.NewReader(stdout))
|
|
||||||
jsdec.DisallowUnknownFields()
|
|
||||||
puzzle := new(Puzzle)
|
|
||||||
if err := jsdec.Decode(puzzle); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open returns a newly-opened file.
|
|
||||||
func (p *Puzzle) Open(name string) (io.ReadCloser, error) {
|
|
||||||
// BUG(neale): You cannot open generated files in puzzles, only files actually on the disk
|
|
||||||
return p.fs.Open(name)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -12,11 +14,12 @@ func TestPuzzle(t *testing.T) {
|
||||||
catFs := afero.NewBasePathFs(puzzleFs, "cat0")
|
catFs := afero.NewBasePathFs(puzzleFs, "cat0")
|
||||||
|
|
||||||
{
|
{
|
||||||
p, err := NewPuzzle(catFs, 1)
|
pd := NewPuzzleDir(catFs, 1)
|
||||||
|
p, err := pd.Export()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
t.Log(p)
|
|
||||||
if (len(p.Answers) == 0) || (p.Answers[0] != "YAML answer") {
|
if (len(p.Answers) == 0) || (p.Answers[0] != "YAML answer") {
|
||||||
t.Error("Answers are wrong", p.Answers)
|
t.Error("Answers are wrong", p.Answers)
|
||||||
}
|
}
|
||||||
|
@ -29,7 +32,7 @@ func TestPuzzle(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
p, err := NewPuzzle(catFs, 2)
|
p, err := NewPuzzleDir(catFs, 2).Export()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -44,26 +47,27 @@ func TestPuzzle(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := NewPuzzle(catFs, 3); err != nil {
|
if _, err := NewPuzzleDir(catFs, 3).Export(); err != nil {
|
||||||
t.Error("Legacy `puzzle.moth` file:", err)
|
t.Error("Legacy `puzzle.moth` file:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := NewPuzzle(catFs, 99); err == nil {
|
if _, err := NewPuzzleDir(catFs, 99).Export(); err == nil {
|
||||||
t.Error("Non-existent puzzle", err)
|
t.Error("Non-existent puzzle", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := NewPuzzle(catFs, 10); err == nil {
|
if _, err := NewPuzzleDir(catFs, 10).Export(); err == nil {
|
||||||
t.Error("Broken YAML")
|
t.Error("Broken YAML")
|
||||||
}
|
}
|
||||||
if _, err := NewPuzzle(catFs, 20); err == nil {
|
if _, err := NewPuzzleDir(catFs, 20).Export(); err == nil {
|
||||||
t.Error("Bad RFC822 header")
|
t.Error("Bad RFC822 header")
|
||||||
}
|
}
|
||||||
if _, err := NewPuzzle(catFs, 21); err == nil {
|
if _, err := NewPuzzleDir(catFs, 21).Export(); err == nil {
|
||||||
t.Error("Boken RFC822 header")
|
t.Error("Boken RFC822 header")
|
||||||
}
|
}
|
||||||
if _, err := NewPuzzle(catFs, 22); err == nil {
|
if p, err := NewPuzzleDir(catFs, 22).Export(); err == nil {
|
||||||
t.Error("Duplicate bodies")
|
t.Error("Duplicate bodies")
|
||||||
} else if !strings.HasPrefix(err.Error(), "Puzzle body present") {
|
} else if !strings.HasPrefix(err.Error(), "Puzzle body present") {
|
||||||
|
t.Log(p)
|
||||||
t.Error("Wrong error for duplicate body:", err)
|
t.Error("Wrong error for duplicate body:", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,11 +75,29 @@ func TestPuzzle(t *testing.T) {
|
||||||
func TestFsPuzzle(t *testing.T) {
|
func TestFsPuzzle(t *testing.T) {
|
||||||
catFs := afero.NewBasePathFs(afero.NewOsFs(), "testdata")
|
catFs := afero.NewBasePathFs(afero.NewOsFs(), "testdata")
|
||||||
|
|
||||||
if _, err := NewPuzzle(catFs, 1); err != nil {
|
if _, err := NewPuzzleDir(catFs, 1).Export(); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := NewPuzzle(catFs, 2); err != nil {
|
if _, err := NewPuzzleDir(catFs, 2).Export(); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mkpuzzleDir := NewPuzzleDir(catFs, 3)
|
||||||
|
if _, err := mkpuzzleDir.Export(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if body, err := mkpuzzleDir.Open("moo.txt"); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else {
|
||||||
|
defer body.Close()
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
if _, err := io.Copy(buf, body); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
if buf.String() != "Moo.\n" {
|
||||||
|
t.Error("Wrong body")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ case $1 in
|
||||||
{
|
{
|
||||||
"Answers": ["answer"],
|
"Answers": ["answer"],
|
||||||
"Pre": {
|
"Pre": {
|
||||||
"Authors": "neale",
|
"Authors": ["neale"],
|
||||||
"Body": "I am a generated puzzle."
|
"Body": "I am a generated puzzle."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue