~bigbes/sr-ht-spec

ref: cb60d4f754f4ea21121414f7e3fc8b10118c8ac4 sr-ht-spec/service/space_test.go -rw-r--r-- 3.2 KiB
cb60d4f7 — Eugene Blikh ci: run the suites against a real Postgres on the builder 9 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package service

import (
	"context"
	"errors"
	"os"
	"testing"

	"sourcecraft.dev/bigbes/sr-ht-spec/core"
	"sourcecraft.dev/bigbes/sr-ht-spec/gitx"
)

// A crash between the repository and the row leaves content that is merely
// unlisted; a *failure* of the row insert leaves nothing at all, because the
// repository is seconds old, empty and named nowhere yet.
func TestCreateSpaceRemovesTheRepositoryWhenTheRowFails(t *testing.T) {
	svc, root := newService(t) // its database handle cannot be reached
	dir := gitx.DiskPath(root, fxSpace)

	_, err := svc.CreateSpace(context.Background(), fxSpace)
	if err == nil {
		t.Fatal("CreateSpace succeeded with an unreachable database")
	}
	if _, statErr := os.Stat(dir); !os.IsNotExist(statErr) {
		t.Fatalf("repository at %s survived a failed row insert (stat: %v)", dir, statErr)
	}
}

func TestCreateSpaceRejectsUnsafeNames(t *testing.T) {
	svc, _ := newService(t)
	ctx := context.Background()
	for _, ref := range []core.SpaceRef{
		{Owner: "..", Name: "rfcs"},
		{Owner: "bigbes", Name: "../../etc"},
		{Owner: "bigbes", Name: ""},
		{Owner: "", Name: "rfcs"},
	} {
		if _, err := svc.CreateSpace(ctx, ref); err == nil {
			t.Errorf("CreateSpace(%+v) succeeded", ref)
		}
	}
}

func TestSpaceLifecycle(t *testing.T) {
	svc, root := newTestService(t)
	ctx := context.Background()

	sp, err := svc.CreateSpace(ctx, fxSpace)
	if err != nil {
		t.Fatalf("CreateSpace: %v", err)
	}
	if sp.ID == 0 {
		t.Error("space has no row id")
	}
	if sp.Repo.Dir() != gitx.DiskPath(root, fxSpace) {
		t.Errorf("dir = %q", sp.Repo.Dir())
	}
	// The approved head resolves from the moment the space exists, so no
	// reader, reconciler or merge has to special-case an unborn branch.
	if _, err := sp.Repo.ApprovedHead(ctx); err != nil {
		t.Errorf("approved head: %v", err)
	}

	if _, err := svc.CreateSpace(ctx, fxSpace); !errors.Is(err, ErrSpaceExists) {
		t.Errorf("second CreateSpace err = %v, want ErrSpaceExists", err)
	}

	opened, err := svc.OpenSpace(ctx, fxSpace)
	if err != nil {
		t.Fatalf("OpenSpace: %v", err)
	}
	if opened.ID != sp.ID || opened.Repo == nil {
		t.Errorf("opened = %+v", opened)
	}

	if _, err := svc.OpenSpace(ctx, core.SpaceRef{Owner: "bigbes", Name: "nope"}); !errors.Is(err, ErrNotFound) {
		t.Errorf("OpenSpace of a missing space err = %v, want ErrNotFound", err)
	}

	spaces, err := svc.ListSpaces(ctx)
	if err != nil {
		t.Fatalf("ListSpaces: %v", err)
	}
	if len(spaces) != 1 || spaces[0].Ref != fxSpace {
		t.Fatalf("spaces = %+v", spaces)
	}
	if spaces[0].Repo != nil {
		t.Error("ListSpaces opened repositories; listing must stay cheap")
	}
}

// A row whose repository is missing is a half-created space, not a space. It is
// reported rather than served: every document read would fail individually.
func TestOpenSpaceRefusesARowWithNoRepository(t *testing.T) {
	svc, root := newTestService(t)
	ctx := context.Background()

	if _, err := svc.CreateSpace(ctx, fxSpace); err != nil {
		t.Fatalf("CreateSpace: %v", err)
	}
	if err := os.RemoveAll(gitx.DiskPath(root, fxSpace)); err != nil {
		t.Fatalf("remove repository: %v", err)
	}
	if _, err := svc.OpenSpace(ctx, fxSpace); !errors.Is(err, ErrNotFound) {
		t.Fatalf("err = %v, want ErrNotFound", err)
	}
}