aboutsummaryrefslogtreecommitdiff
package fetch_test

import (
	"errors"
	"io/fs"
	"testing"

	"lindenii.org/go/furgit/object/commit"
	"lindenii.org/go/furgit/object/fetch"
	"lindenii.org/go/furgit/object/id"
	"lindenii.org/go/furgit/object/signature"
	"lindenii.org/go/furgit/object/store/memory"
	"lindenii.org/go/furgit/object/tree"
	"lindenii.org/go/furgit/object/tree/mode"
	"lindenii.org/go/furgit/object/typ"
)

func TestTreeFS(t *testing.T) {
	t.Parallel()

	for _, objectFormat := range id.SupportedObjectFormats() {
		t.Run(objectFormat.String(), func(t *testing.T) {
			t.Parallel()

			store := memory.New(objectFormat)

			plainID, err := store.WriteBytesContent(typ.Blob, []byte("plain\n"))
			if err != nil {
				t.Fatalf("WriteBytesContent(plain.txt): %v", err)
			}

			execID, err := store.WriteBytesContent(typ.Blob, []byte("#!/bin/sh\nexit 0\n"))
			if err != nil {
				t.Fatalf("WriteBytesContent(exec.sh): %v", err)
			}

			subTreeID := writeTree(t, store, []tree.Entry{
				{Mode: mode.Executable, Name: []byte("exec.sh"), ID: execID},
			})

			rootTreeID := writeTree(t, store, []tree.Entry{
				{Mode: mode.Regular, Name: []byte("plain.txt"), ID: plainID},
				{Mode: mode.Directory, Name: []byte("dir"), ID: subTreeID},
			})

			commitID := writeCommit(t, store, rootTreeID)

			fetcher := fetch.New(store)

			treeFS, err := fetcher.TreeFS(commitID)
			if err != nil {
				t.Fatalf("fetcher.TreeFS: %v", err)
			}

			content, err := treeFS.ReadFile("plain.txt")
			if err != nil {
				t.Fatalf("ReadFile(plain.txt): %v", err)
			}

			if string(content) != "plain\n" {
				t.Fatalf("ReadFile(plain.txt) = %q, want %q", string(content), "plain\n")
			}

			entries, err := treeFS.ReadDir(".")
			if err != nil {
				t.Fatalf("ReadDir(.): %v", err)
			}

			if len(entries) != 2 {
				t.Fatalf("len(ReadDir(.)) = %d, want 2", len(entries))
			}

			info, err := treeFS.Stat("plain.txt")
			if err != nil {
				t.Fatalf("Stat(plain.txt): %v", err)
			}

			entry, ok := info.Sys().(tree.Entry)
			if !ok {
				t.Fatalf("Stat(plain.txt).Sys() type = %T, want tree.Entry", info.Sys())
			}

			if entry.Mode != mode.Regular {
				t.Fatalf("Stat(plain.txt).Sys().Mode = %o, want %o", entry.Mode, mode.Regular)
			}

			subFS, err := treeFS.Sub("dir")
			if err != nil {
				t.Fatalf("Sub(dir): %v", err)
			}

			subReadFileFS, ok := subFS.(fs.ReadFileFS)
			if !ok {
				t.Fatalf("Sub(dir) type does not implement fs.ReadFileFS")
			}

			subContent, err := subReadFileFS.ReadFile("exec.sh")
			if err != nil {
				t.Fatalf("Sub(dir).ReadFile(exec.sh): %v", err)
			}

			if string(subContent) != "#!/bin/sh\nexit 0\n" {
				t.Fatalf("Sub(dir).ReadFile(exec.sh) = %q", string(subContent))
			}

			_, err = treeFS.ReadFile("dir")
			if err == nil {
				t.Fatal("ReadFile(dir) unexpectedly succeeded")
			}

			if _, ok := errors.AsType[*fs.PathError](err); !ok {
				t.Fatalf("ReadFile(dir) err type = %T, want *fs.PathError", err)
			}
		})
	}
}

// writeTree builds a tree object from entries, writes it to store,
// and returns its object ID.
func writeTree(t *testing.T, store *memory.Memory, entries []tree.Entry) id.ObjectID {
	t.Helper()

	tr := new(tree.Tree)
	for _, entry := range entries {
		err := tr.Insert(entry)
		if err != nil {
			t.Fatalf("tree.Insert(%q): %v", entry.Name, err)
		}
	}

	body, err := tr.AppendWithoutHeader(nil)
	if err != nil {
		t.Fatalf("tree.AppendWithoutHeader: %v", err)
	}

	treeID, err := store.WriteBytesContent(typ.Tree, body)
	if err != nil {
		t.Fatalf("WriteBytesContent(tree): %v", err)
	}

	return treeID
}

// writeCommit builds a commit object pointing at tree, writes it to store,
// and returns its object ID.
func writeCommit(t *testing.T, store *memory.Memory, tree id.ObjectID) id.ObjectID {
	t.Helper()

	who := signature.Signature{
		Name:          []byte("Test Author"),
		Email:         []byte("author@example.org"),
		WhenUnix:      1234567890,
		OffsetMinutes: 0,
	}

	body, err := (&commit.Commit{
		Tree:      tree,
		Author:    who,
		Committer: who,
		Message:   []byte("treefs\n"),
	}).AppendWithoutHeader(nil)
	if err != nil {
		t.Fatalf("commit.AppendWithoutHeader: %v", err)
	}

	commitID, err := store.WriteBytesContent(typ.Commit, body)
	if err != nil {
		t.Fatalf("WriteBytesContent(commit): %v", err)
	}

	return commitID
}