Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

ns_test.gno

6.26 Kb · 209 lines
  1package ns
  2
  3import (
  4	"strings"
  5	"testing"
  6
  7	"gno.land/p/nt/testutils/v0"
  8	"gno.land/p/nt/uassert/v0"
  9
 10	ninep "gno.land/p/nym-moultest123/x/plan9/ninep/v0"
 11	synfs "gno.land/p/nym-moultest123/x/plan9/synfs/v0"
 12)
 13
 14// probe is a tiny read-only server, standing in for another realm's tree.
 15func probe(name, contents string) ninep.File {
 16	t := synfs.New(name, "sys", func() int64 { return 1 })
 17	t.Root().Add("value", func() string { return contents })
 18	return t.Root()
 19}
 20
 21func TestDemoNamespaceIsSeeded(t *testing.T) {
 22	seedDemo()
 23	out, err := Run(DemoKey, "ls -u /bin")
 24	if err != nil {
 25		t.Fatalf("ls: %v", err)
 26	}
 27	if out != "ls\nrc\n" {
 28		t.Errorf("the demo /bin should be a union of two directories: %q", out)
 29	}
 30	if got := Namespace(DemoKey); !strings.Contains(got, "bind -ac /usr/glenda/bin /bin") {
 31		t.Errorf("mount table: %q", got)
 32	}
 33	if got, _ := Run(DemoKey, "cat /tmp/greeting"); got != "hello from a namespace\n" {
 34		t.Errorf("greeting: %q", got)
 35	}
 36}
 37
 38func TestRunIsReadOnly(t *testing.T) {
 39	seedDemo()
 40	if _, err := Run(DemoKey, "echo x > /tmp/f"); err == nil {
 41		t.Fatal("Render's shell must refuse a write")
 42	}
 43	if _, err := Run(DemoKey, "rm /tmp/greeting"); err == nil {
 44		t.Fatal("Render's shell must refuse a remove")
 45	}
 46	if _, err := Run("nobody", "ls /"); err == nil {
 47		t.Fatal("an unknown namespace should not be created by a read")
 48	}
 49}
 50
 51func TestPostAndBind(cur realm, t *testing.T) {
 52	services.Remove("probe")
 53	Post(cross(cur), "probe", probe("probe", "42"))
 54
 55	found := false
 56	for _, s := range Services() {
 57		if s == "probe" {
 58			found = true
 59		}
 60	}
 61	if !found {
 62		t.Fatalf("probe is not in /srv: %v", Services())
 63	}
 64
 65	// A fresh namespace sees it through /srv without importing anything.
 66	spaces.Remove("tester")
 67	spaceFor("tester")
 68	defer spaces.Remove("tester")
 69	out, err := Run("tester", "cat /srv/probe/value")
 70	if err != nil {
 71		t.Fatalf("cat through /srv: %v", err)
 72	}
 73	if out != "42" {
 74		t.Errorf("got %q, want 42", out)
 75	}
 76	services.Remove("probe")
 77}
 78
 79func TestExecWritesToTheCallersOwnNamespace(cur realm, t *testing.T) {
 80	alice := testutils.TestAddress("alice")
 81	bob := testutils.TestAddress("bob")
 82	spaces.Remove(alice.String())
 83	spaces.Remove(bob.String())
 84
 85	testing.SetRealm(testing.NewUserRealm(alice))
 86	Exec(cross(cur), "echo 'alice was here' > /tmp/note")
 87
 88	testing.SetRealm(testing.NewUserRealm(bob))
 89	Exec(cross(cur), "echo 'bob was here' > /tmp/note")
 90
 91	got, err := Run(alice.String(), "cat /tmp/note")
 92	if err != nil {
 93		t.Fatalf("alice: %v", err)
 94	}
 95	if got != "alice was here\n" {
 96		t.Errorf("alice's namespace: %q", got)
 97	}
 98	if got, _ = Run(bob.String(), "cat /tmp/note"); got != "bob was here\n" {
 99		t.Errorf("bob's namespace: %q", got)
100	}
101
102	// Reset throws a namespace away; the next use rebuilds the default.
103	testing.SetRealm(testing.NewUserRealm(alice))
104	Reset(cross(cur))
105	if _, err := Run(alice.String(), "cat /tmp/note"); err == nil {
106		t.Error("the namespace should be gone after Reset")
107	}
108	spaces.Remove(alice.String())
109	spaces.Remove(bob.String())
110}
111
112func TestExecBindsIntoTheCallersNamespace(cur realm, t *testing.T) {
113	services.Remove("probe")
114	Post(cross(cur), "probe", probe("probe", "42"))
115	defer services.Remove("probe")
116
117	erin := testutils.TestAddress("erin")
118	spaces.Remove(erin.String())
119	testing.SetRealm(testing.NewUserRealm(erin))
120	Exec(cross(cur), "bind /srv/probe /dev")
121
122	if got := mustRun(t, erin.String(), "cat /dev/value"); got != "42" {
123		t.Errorf("read through the mount: %q", got)
124	}
125	if ns := Namespace(erin.String()); !strings.Contains(ns, "bind /srv/probe /dev") {
126		t.Errorf("mount table: %q", ns)
127	}
128	spaces.Remove(erin.String())
129}
130
131func TestSrvListingUsesThePostedName(cur realm, t *testing.T) {
132	services.Remove("aliased")
133	// The server calls its own root "probe"; /srv must show it as "aliased".
134	Post(cross(cur), "aliased", probe("probe", "x"))
135	defer services.Remove("aliased")
136
137	spaces.Remove("lister")
138	spaceFor("lister")
139	out := mustRun(t, "lister", "ls /srv")
140	if !strings.Contains(out, "aliased") {
141		t.Errorf("ls /srv: %q", out)
142	}
143	if strings.Contains(out, "probe") {
144		t.Errorf("/srv leaked the server's own root name: %q", out)
145	}
146	spaces.Remove("lister")
147}
148
149func mustRun(t *testing.T, key, line string) string {
150	t.Helper()
151	out, err := Run(key, line)
152	if err != nil {
153		t.Fatalf("%s: %v", line, err)
154	}
155	return out
156}
157
158// --- the boundary. Three properties the suite would be unsafe without. -------
159
160// A /srv name belongs to the realm that posted it: first come, first served,
161// and then locked.
162func TestPostRejectsASecondOwner(cur realm, t *testing.T) {
163	services.Remove("taken")
164	Post(cross(cur), "taken", probe("taken", "first"))
165	defer services.Remove("taken")
166
167	testing.SetRealm(testing.NewCodeRealm("gno.land/r/other/thing/v0"))
168	uassert.AbortsWithMessage(t, cur,
169		"post: taken is already posted by gno.land/r/nym-moultest123/x/plan9/ns/v0",
170		func() { Post(cross(cur), "taken", probe("taken", "second")) })
171}
172
173// Grafting another realm's tree into your namespace gives you reads and
174// nothing else. ninep.File has no mutating method, so there is no route from a
175// mount to a write against the realm that posted it.
176func TestMountedTreesAreReadOnly(cur realm, t *testing.T) {
177	services.Remove("probe")
178	Post(cross(cur), "probe", probe("probe", "read me"))
179	defer services.Remove("probe")
180
181	frank := testutils.TestAddress("frank")
182	spaces.Remove(frank.String())
183	defer spaces.Remove(frank.String())
184
185	testing.SetRealm(testing.NewUserRealm(frank))
186	Exec(cross(cur), "bind /srv/probe /dev")
187	uassert.Equal(t, "read me", mustRun(t, frank.String(), "cat /dev/value"))
188	uassert.AbortsWithMessage(t, cur, "echo: read-only file server",
189		func() { Exec(cross(cur), "echo nope > /dev/value") })
190}
191
192// A command line stops at the first error and the error leaves the realm as an
193// abort, so a half-applied Exec reverts with its transaction instead of
194// leaving a namespace nobody asked for.
195func TestExecAbortsAtTheFirstError(cur realm, t *testing.T) {
196	grace := testutils.TestAddress("grace")
197	spaces.Remove(grace.String())
198	defer spaces.Remove(grace.String())
199
200	testing.SetRealm(testing.NewUserRealm(grace))
201	uassert.AbortsWithMessage(t, cur, "cat: file does not exist",
202		func() { Exec(cross(cur), "echo ok > /tmp/a; cat /absent; echo never > /tmp/b") })
203}
204
205func TestRenderUnknownCommand(t *testing.T) {
206	if got := Render("nope/x"); !strings.Contains(got, "404") {
207		t.Errorf("got %q", got)
208	}
209}