oficina / docs Public

Clone
git clone https://amadan.net/oficina/docs

Plain git — no account needed to clone.

Download

Download this file

1package docs_test
2
3import (
4 "context"
5 "path/filepath"
6 "strings"
7 "testing"
8
9 "github.com/carlosframework/docs/internal/docs"
10)
11
12// newSampleDocumentsTestApp builds a development App exactly like
13// docs.NewForTest, except with Config.SampleDocuments set explicitly
14// — the same "build directly via New, package-internal test helper"
15// shape auth_allowlist_test.go's newAllowlistedTestApp uses for the
16// same reason: NewForTest itself never sets this, so any test that
17// wants seeding on (or explicitly off) has to build its own App.
18func newSampleDocumentsTestApp(t *testing.T, sampleDocuments string) *docs.App {
19 t.Helper()
20 app, err := docs.New(docs.Config{
21 DBPath: filepath.Join(t.TempDir(), "docs.db"),
22 Env: "development",
23 SampleDocuments: sampleDocuments,
24 })
25 if err != nil {
26 t.Fatalf("New: %v", err)
27 }
28 t.Cleanup(func() {
29 if cerr := app.Close(); cerr != nil {
30 t.Errorf("App.Close: %v", cerr)
31 }
32 })
33 return app
34}
35
36// TestNewPersonWithSampleDocumentsOnGetsExactlySix pins the design
37// this branch exists to ship: a brand-new person's very first
38// EnsurePerson call, on an instance that opted in via
39// $DOCS_SAMPLE_DOCUMENTS, gets seed.go's own sample set — exactly six
40// documents (len(seedDocs) in seed.go), owned by them, each with real,
41// substantial content read back through the actual CRDT, never a row
42// count standing in for content that was never applied.
43//
44// Read through Store.HTML, not Store.Text: the seed generator now
45// builds a Y.XmlFragment on root "prosemirror" (fixtures/generate-seed.mjs),
46// so a seeded document's Store.Text legitimately replays to "" — HTML
47// is the read path that actually replays what Seed wrote.
48func TestNewPersonWithSampleDocumentsOnGetsExactlySix(t *testing.T) {
49 app := newSampleDocumentsTestApp(t, "1")
50 ctx := context.Background()
51
52 owner, err := app.Store().EnsurePerson(ctx, "brand-new@example.com")
53 if err != nil {
54 t.Fatalf("EnsurePerson: %v", err)
55 }
56
57 list, err := app.Store().List(ctx, owner)
58 if err != nil {
59 t.Fatalf("List: %v", err)
60 }
61 if len(list) != 6 {
62 t.Fatalf("got %d documents, want exactly 6", len(list))
63 }
64
65 for _, d := range list {
66 html, err := app.Store().HTML(ctx, d)
67 if err != nil {
68 t.Fatalf("HTML(%q): %v", d.Title, err)
69 }
70 if len(strings.TrimSpace(string(html))) < 200 {
71 t.Errorf("%q has %d characters of HTML — want substantial, real content", d.Title, len(html))
72 }
73 if d.OwnerID != owner {
74 t.Errorf("%q has OwnerID %d, want %d", d.Title, d.OwnerID, owner)
75 }
76 }
77}
78
79// TestNewPersonWithSampleDocumentsOffGetsNone is the opt-in's default:
80// with $DOCS_SAMPLE_DOCUMENTS unset (SampleDocuments left "" here,
81// exactly what a real deployment gets if nobody has set the variable
82// at all), a brand-new person's list starts empty — the pre-existing,
83// unchanged behaviour every real team instance must keep.
84func TestNewPersonWithSampleDocumentsOffGetsNone(t *testing.T) {
85 app := newSampleDocumentsTestApp(t, "")
86 ctx := context.Background()
87
88 owner, err := app.Store().EnsurePerson(ctx, "brand-new@example.com")
89 if err != nil {
90 t.Fatalf("EnsurePerson: %v", err)
91 }
92
93 list, err := app.Store().List(ctx, owner)
94 if err != nil {
95 t.Fatalf("List: %v", err)
96 }
97 if len(list) != 0 {
98 t.Fatalf("got %d documents with SampleDocuments off, want 0", len(list))
99 }
100}
101
102// TestExistingPersonSigningInAgainGetsNoNewDocuments is the seam this
103// whole design hangs off: EnsurePerson's create branch seeds, its
104// found branch (Person row already exists) never does. Calling
105// EnsurePerson a second time for the same address must not grow the
106// document count at all, whether or not sample content already
107// landed on the first call.
108func TestExistingPersonSigningInAgainGetsNoNewDocuments(t *testing.T) {
109 app := newSampleDocumentsTestApp(t, "true")
110 ctx := context.Background()
111
112 owner, err := app.Store().EnsurePerson(ctx, "returning@example.com")
113 if err != nil {
114 t.Fatalf("EnsurePerson (first): %v", err)
115 }
116 first, err := app.Store().List(ctx, owner)
117 if err != nil {
118 t.Fatalf("List (first): %v", err)
119 }
120 if len(first) != 6 {
121 t.Fatalf("got %d documents after first sign-in, want 6", len(first))
122 }
123
124 ownerAgain, err := app.Store().EnsurePerson(ctx, "returning@example.com")
125 if err != nil {
126 t.Fatalf("EnsurePerson (second): %v", err)
127 }
128 if ownerAgain != owner {
129 t.Fatalf("EnsurePerson(again) = %d, want the same person id %d", ownerAgain, owner)
130 }
131
132 second, err := app.Store().List(ctx, owner)
133 if err != nil {
134 t.Fatalf("List (second): %v", err)
135 }
136 if len(second) != len(first) {
137 t.Fatalf("document count grew from %d to %d on a repeat sign-in", len(first), len(second))
138 }
139}
140
141// TestTwoPeopleEachGetTheirOwnSampleSet checks seeding does not
142// undermine owned() (store.go): two different brand-new people each
143// get their own full set of six, and List for one never includes a
144// row belonging to the other — the same ownership guarantee
145// TestOwnedScopesByOwner (scope_test.go) pins for hand-created
146// documents, exercised here through the seeding path instead.
147func TestTwoPeopleEachGetTheirOwnSampleSet(t *testing.T) {
148 app := newSampleDocumentsTestApp(t, "yes")
149 ctx := context.Background()
150
151 alice, err := app.Store().EnsurePerson(ctx, "alice-sample@example.com")
152 if err != nil {
153 t.Fatalf("EnsurePerson(alice): %v", err)
154 }
155 bob, err := app.Store().EnsurePerson(ctx, "bob-sample@example.com")
156 if err != nil {
157 t.Fatalf("EnsurePerson(bob): %v", err)
158 }
159 if alice == bob {
160 t.Fatalf("alice and bob resolved to the same person id %d", alice)
161 }
162
163 aliceDocs, err := app.Store().List(ctx, alice)
164 if err != nil {
165 t.Fatalf("List(alice): %v", err)
166 }
167 bobDocs, err := app.Store().List(ctx, bob)
168 if err != nil {
169 t.Fatalf("List(bob): %v", err)
170 }
171 if len(aliceDocs) != 6 {
172 t.Fatalf("alice has %d documents, want 6", len(aliceDocs))
173 }
174 if len(bobDocs) != 6 {
175 t.Fatalf("bob has %d documents, want 6", len(bobDocs))
176 }
177
178 for _, d := range aliceDocs {
179 if d.OwnerID != alice {
180 t.Errorf("alice's List returned a document owned by %d, not %d", d.OwnerID, alice)
181 }
182 if _, err := app.Store().Get(ctx, bob, d.Sqid); err == nil {
183 t.Errorf("bob can Get alice's document %q — owned() is not isolating seeded documents", d.Sqid)
184 }
185 }
186 for _, d := range bobDocs {
187 if d.OwnerID != bob {
188 t.Errorf("bob's List returned a document owned by %d, not %d", d.OwnerID, bob)
189 }
190 if _, err := app.Store().Get(ctx, alice, d.Sqid); err == nil {
191 t.Errorf("alice can Get bob's document %q — owned() is not isolating seeded documents", d.Sqid)
192 }
193 }
194}
195