Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Vanadium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 5 | package nosql_test |
| 6 | |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 7 | import ( |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 8 | "reflect" |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 9 | "testing" |
Sergey Rogulenko | 1f988de | 2015-08-14 17:00:09 -0700 | [diff] [blame] | 10 | "time" |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 11 | |
Sergey Rogulenko | 1f988de | 2015-08-14 17:00:09 -0700 | [diff] [blame] | 12 | wire "v.io/syncbase/v23/services/syncbase/nosql" |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 13 | "v.io/syncbase/v23/syncbase" |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 14 | "v.io/syncbase/v23/syncbase/nosql" |
John Kline | 4894fb1 | 2015-06-17 09:06:56 -0700 | [diff] [blame] | 15 | "v.io/syncbase/v23/syncbase/nosql/syncql" |
Adam Sadovsky | fa5f16f | 2015-05-04 15:33:22 -0700 | [diff] [blame] | 16 | tu "v.io/syncbase/v23/syncbase/testutil" |
Sergey Rogulenko | 1f988de | 2015-08-14 17:00:09 -0700 | [diff] [blame] | 17 | "v.io/v23/context" |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 18 | "v.io/v23/naming" |
Sergey Rogulenko | 1f988de | 2015-08-14 17:00:09 -0700 | [diff] [blame] | 19 | "v.io/v23/services/watch" |
John Kline | 4894fb1 | 2015-06-17 09:06:56 -0700 | [diff] [blame] | 20 | "v.io/v23/vdl" |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 21 | "v.io/v23/verror" |
Sergey Rogulenko | 1f988de | 2015-08-14 17:00:09 -0700 | [diff] [blame] | 22 | "v.io/v23/vom" |
Adam Sadovsky | 67e5f7c | 2015-05-11 16:04:15 -0700 | [diff] [blame] | 23 | _ "v.io/x/ref/runtime/factories/generic" |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 24 | ) |
| 25 | |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 26 | // TODO(sadovsky): Finish writing tests. |
Sergey Rogulenko | 445e041 | 2015-06-18 23:06:11 -0700 | [diff] [blame] | 27 | // TODO(rogulenko): Test perms checking for Glob and Exec. |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 28 | |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 29 | // Tests various Name, FullName, and Key methods. |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 30 | func TestNameAndKey(t *testing.T) { |
Jatin Lodhia | af93faa | 2015-07-17 15:57:36 -0700 | [diff] [blame] | 31 | d := syncbase.NewService("s").App("a").NoSQLDatabase("d", nil) |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 32 | tb := d.Table("tb") |
| 33 | r := tb.Row("r") |
| 34 | |
| 35 | if d.Name() != "d" { |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 36 | t.Errorf("Wrong name: %q", d.Name()) |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 37 | } |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 38 | if d.FullName() != naming.Join("s", "a", "d") { |
| 39 | t.Errorf("Wrong full name: %q", d.FullName()) |
| 40 | } |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 41 | if tb.Name() != "tb" { |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 42 | t.Errorf("Wrong name: %q", tb.Name()) |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 43 | } |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 44 | if tb.FullName() != naming.Join("s", "a", "d", "tb") { |
| 45 | t.Errorf("Wrong full name: %q", tb.FullName()) |
| 46 | } |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 47 | if r.Key() != "r" { |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 48 | t.Errorf("Wrong key: %q", r.Key()) |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 49 | } |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 50 | if r.FullName() != naming.Join("s", "a", "d", "tb", "r") { |
| 51 | t.Errorf("Wrong full name: %q", r.FullName()) |
| 52 | } |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 53 | } |
| 54 | |
Adam Sadovsky | fa5f16f | 2015-05-04 15:33:22 -0700 | [diff] [blame] | 55 | // Tests that Database.Create works as expected. |
| 56 | func TestDatabaseCreate(t *testing.T) { |
| 57 | ctx, sName, cleanup := tu.SetupOrDie(nil) |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 58 | defer cleanup() |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 59 | a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a") |
Adam Sadovsky | fa5f16f | 2015-05-04 15:33:22 -0700 | [diff] [blame] | 60 | tu.TestCreate(t, ctx, a) |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 61 | } |
| 62 | |
John Kline | 4894fb1 | 2015-06-17 09:06:56 -0700 | [diff] [blame] | 63 | // Tests that Database.Exec works as expected. |
| 64 | // Note: More comprehensive client/server tests are in the exec_test |
| 65 | // directory. Also, exec is tested in its entirety in |
| 66 | // v23/syncbase/nosql/internal/query/... |
| 67 | func TestExec(t *testing.T) { |
| 68 | ctx, sName, cleanup := tu.SetupOrDie(nil) |
| 69 | defer cleanup() |
| 70 | a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a") |
| 71 | d := tu.CreateNoSQLDatabase(t, ctx, a, "d") |
| 72 | tb := tu.CreateTable(t, ctx, d, "tb") |
| 73 | |
| 74 | foo := Foo{I: 4, S: "f"} |
| 75 | if err := tb.Put(ctx, "foo", foo); err != nil { |
| 76 | t.Fatalf("tb.Put() failed: %v", err) |
| 77 | } |
| 78 | |
| 79 | bar := Bar{F: 0.5, S: "b"} |
| 80 | // NOTE: not best practice, but store bar as |
| 81 | // optional (by passing the address of bar to Put). |
| 82 | // This tests auto-dereferencing. |
| 83 | if err := tb.Put(ctx, "bar", &bar); err != nil { |
| 84 | t.Fatalf("tb.Put() failed: %v", err) |
| 85 | } |
| 86 | |
| 87 | baz := Baz{Name: "John Doe", Active: true} |
| 88 | if err := tb.Put(ctx, "baz", baz); err != nil { |
| 89 | t.Fatalf("tb.Put() failed: %v", err) |
| 90 | } |
| 91 | |
John Kline | 6bf4e90 | 2015-08-03 13:28:41 -0700 | [diff] [blame] | 92 | tu.CheckExec(t, ctx, d, "select k, v.Name from tb where Type(v) like \"%.Baz\"", |
John Kline | 4894fb1 | 2015-06-17 09:06:56 -0700 | [diff] [blame] | 93 | []string{"k", "v.Name"}, |
| 94 | [][]*vdl.Value{ |
| 95 | []*vdl.Value{vdl.ValueOf("baz"), vdl.ValueOf(baz.Name)}, |
| 96 | }) |
| 97 | |
| 98 | tu.CheckExec(t, ctx, d, "select k, v from tb", |
| 99 | []string{"k", "v"}, |
| 100 | [][]*vdl.Value{ |
| 101 | []*vdl.Value{vdl.ValueOf("bar"), vdl.ValueOf(bar)}, |
| 102 | []*vdl.Value{vdl.ValueOf("baz"), vdl.ValueOf(baz)}, |
| 103 | []*vdl.Value{vdl.ValueOf("foo"), vdl.ValueOf(foo)}, |
| 104 | }) |
| 105 | |
| 106 | tu.CheckExec(t, ctx, d, "select k, v from tb where k like \"ba%\"", |
| 107 | []string{"k", "v"}, |
| 108 | [][]*vdl.Value{ |
| 109 | []*vdl.Value{vdl.ValueOf("bar"), vdl.ValueOf(bar)}, |
| 110 | []*vdl.Value{vdl.ValueOf("baz"), vdl.ValueOf(baz)}, |
| 111 | }) |
| 112 | |
| 113 | tu.CheckExec(t, ctx, d, "select k, v from tb where v.Active = true", |
| 114 | []string{"k", "v"}, |
| 115 | [][]*vdl.Value{ |
| 116 | []*vdl.Value{vdl.ValueOf("baz"), vdl.ValueOf(baz)}, |
| 117 | }) |
| 118 | |
John Kline | 6bf4e90 | 2015-08-03 13:28:41 -0700 | [diff] [blame] | 119 | tu.CheckExec(t, ctx, d, "select k, v from tb where Type(v) like \"%.Bar\"", |
John Kline | 4894fb1 | 2015-06-17 09:06:56 -0700 | [diff] [blame] | 120 | []string{"k", "v"}, |
| 121 | [][]*vdl.Value{ |
| 122 | []*vdl.Value{vdl.ValueOf("bar"), vdl.ValueOf(bar)}, |
| 123 | }) |
| 124 | |
| 125 | tu.CheckExec(t, ctx, d, "select k, v from tb where v.F = 0.5", |
| 126 | []string{"k", "v"}, |
| 127 | [][]*vdl.Value{ |
| 128 | []*vdl.Value{vdl.ValueOf("bar"), vdl.ValueOf(bar)}, |
| 129 | }) |
| 130 | |
John Kline | 6bf4e90 | 2015-08-03 13:28:41 -0700 | [diff] [blame] | 131 | tu.CheckExec(t, ctx, d, "select k, v from tb where Type(v) like \"%.Baz\"", |
John Kline | 4894fb1 | 2015-06-17 09:06:56 -0700 | [diff] [blame] | 132 | []string{"k", "v"}, |
| 133 | [][]*vdl.Value{ |
| 134 | []*vdl.Value{vdl.ValueOf("baz"), vdl.ValueOf(baz)}, |
| 135 | }) |
| 136 | |
| 137 | tu.CheckExecError(t, ctx, d, "select k, v from foo", syncql.ErrTableCantAccess.ID) |
| 138 | } |
| 139 | |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 140 | // Tests that Database.Delete works as expected. |
Adam Sadovsky | fa5f16f | 2015-05-04 15:33:22 -0700 | [diff] [blame] | 141 | func TestDatabaseDelete(t *testing.T) { |
| 142 | ctx, sName, cleanup := tu.SetupOrDie(nil) |
| 143 | defer cleanup() |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 144 | a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a") |
Adam Sadovsky | fa5f16f | 2015-05-04 15:33:22 -0700 | [diff] [blame] | 145 | tu.TestDelete(t, ctx, a) |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 146 | } |
| 147 | |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 148 | // Tests that Database.ListTables works as expected. |
| 149 | func TestListTables(t *testing.T) { |
| 150 | ctx, sName, cleanup := tu.SetupOrDie(nil) |
| 151 | defer cleanup() |
| 152 | a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a") |
| 153 | d := tu.CreateNoSQLDatabase(t, ctx, a, "d") |
| 154 | tu.TestListChildren(t, ctx, d) |
| 155 | } |
| 156 | |
Adam Sadovsky | fa5f16f | 2015-05-04 15:33:22 -0700 | [diff] [blame] | 157 | // Tests that Database.{Set,Get}Permissions work as expected. |
| 158 | func TestDatabasePerms(t *testing.T) { |
| 159 | ctx, sName, cleanup := tu.SetupOrDie(nil) |
| 160 | defer cleanup() |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 161 | a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a") |
| 162 | d := tu.CreateNoSQLDatabase(t, ctx, a, "d") |
Adam Sadovsky | fa5f16f | 2015-05-04 15:33:22 -0700 | [diff] [blame] | 163 | tu.TestPerms(t, ctx, d) |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 164 | } |
| 165 | |
Adam Sadovsky | fa5f16f | 2015-05-04 15:33:22 -0700 | [diff] [blame] | 166 | // Tests that Database.CreateTable works as expected. |
| 167 | func TestTableCreate(t *testing.T) { |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 168 | ctx, sName, cleanup := tu.SetupOrDie(nil) |
| 169 | defer cleanup() |
| 170 | a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a") |
| 171 | d := tu.CreateNoSQLDatabase(t, ctx, a, "d") |
| 172 | tu.TestCreate(t, ctx, d) |
Adam Sadovsky | fa5f16f | 2015-05-04 15:33:22 -0700 | [diff] [blame] | 173 | } |
| 174 | |
| 175 | // Tests that Database.DeleteTable works as expected. |
| 176 | func TestTableDelete(t *testing.T) { |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 177 | ctx, sName, cleanup := tu.SetupOrDie(nil) |
| 178 | defer cleanup() |
| 179 | a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a") |
| 180 | d := tu.CreateNoSQLDatabase(t, ctx, a, "d") |
| 181 | tu.TestDelete(t, ctx, d) |
Adam Sadovsky | fa5f16f | 2015-05-04 15:33:22 -0700 | [diff] [blame] | 182 | } |
| 183 | |
| 184 | // Tests that Table.{Set,Get,Delete}Permissions methods work as expected. |
| 185 | func TestTablePerms(t *testing.T) { |
Adam Sadovsky | da06920 | 2015-06-24 12:56:11 -0700 | [diff] [blame] | 186 | ctx, clientACtx, sName, rootp, cleanup := tu.SetupOrDieCustom("clientA", "server", nil) |
Sergey Rogulenko | 445e041 | 2015-06-18 23:06:11 -0700 | [diff] [blame] | 187 | defer cleanup() |
Adam Sadovsky | da06920 | 2015-06-24 12:56:11 -0700 | [diff] [blame] | 188 | clientBCtx := tu.NewCtx(ctx, rootp, "clientB") |
Sergey Rogulenko | 445e041 | 2015-06-18 23:06:11 -0700 | [diff] [blame] | 189 | a := tu.CreateApp(t, clientACtx, syncbase.NewService(sName), "a") |
| 190 | d := tu.CreateNoSQLDatabase(t, clientACtx, a, "d") |
| 191 | tb := tu.CreateTable(t, clientACtx, d, "tb") |
| 192 | |
| 193 | // Permission objects. |
Adam Sadovsky | da06920 | 2015-06-24 12:56:11 -0700 | [diff] [blame] | 194 | aAndB := tu.DefaultPerms("root/clientA", "root/clientB") |
| 195 | aOnly := tu.DefaultPerms("root/clientA") |
| 196 | bOnly := tu.DefaultPerms("root/clientB") |
Sergey Rogulenko | 445e041 | 2015-06-18 23:06:11 -0700 | [diff] [blame] | 197 | |
| 198 | // Set initial permissions. |
| 199 | if err := tb.SetPermissions(clientACtx, nosql.Prefix(""), aAndB); err != nil { |
| 200 | t.Fatalf("tb.SetPermissions() failed: %v", err) |
| 201 | } |
| 202 | if err := tb.SetPermissions(clientACtx, nosql.Prefix("prefix"), aAndB); err != nil { |
| 203 | t.Fatalf("tb.SetPermissions() failed: %v", err) |
| 204 | } |
| 205 | if err := tb.SetPermissions(clientBCtx, nosql.Prefix("prefix"), aAndB); err != nil { |
| 206 | t.Fatalf("tb.SetPermissions() failed: %v", err) |
| 207 | } |
| 208 | if err := tb.SetPermissions(clientACtx, nosql.Prefix("prefix_a"), aOnly); err != nil { |
| 209 | t.Fatalf("tb.SetPermissions() failed: %v", err) |
| 210 | } |
| 211 | if err := tb.SetPermissions(clientBCtx, nosql.Prefix("prefix_b"), bOnly); err != nil { |
| 212 | t.Fatalf("tb.SetPermissions() failed: %v", err) |
| 213 | } |
| 214 | |
| 215 | // Checks A has no access to 'prefix_b' and vice versa. |
| 216 | if err := tb.SetPermissions(clientACtx, nosql.Prefix("prefix_b"), aOnly); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 217 | t.Fatalf("tb.SetPermissions() should have failed: %v", err) |
| 218 | } |
| 219 | if err := tb.SetPermissions(clientACtx, nosql.Prefix("prefix_b_suffix"), aOnly); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 220 | t.Fatalf("tb.SetPermissions() should have failed: %v", err) |
| 221 | } |
| 222 | if err := tb.SetPermissions(clientBCtx, nosql.Prefix("prefix_a"), bOnly); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 223 | t.Fatalf("tb.SetPermissions() should have failed: %v", err) |
| 224 | } |
| 225 | if err := tb.SetPermissions(clientBCtx, nosql.Prefix("prefix_a_suffix"), bOnly); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 226 | t.Fatalf("tb.SetPermissions() should have failed: %v", err) |
| 227 | } |
| 228 | |
| 229 | // Check GetPermissions. |
| 230 | wantPerms := []nosql.PrefixPermissions{ |
| 231 | nosql.PrefixPermissions{Prefix: nosql.Prefix(""), Perms: aAndB}, |
| 232 | } |
| 233 | if got, _ := tb.GetPermissions(clientACtx, ""); !reflect.DeepEqual(got, wantPerms) { |
| 234 | t.Fatalf("Unexpected permissions: got %v, want %v", got, wantPerms) |
| 235 | } |
| 236 | if got, _ := tb.GetPermissions(clientACtx, "abc"); !reflect.DeepEqual(got, wantPerms) { |
| 237 | t.Fatalf("Unexpected permissions: got %v, want %v", got, wantPerms) |
| 238 | } |
| 239 | wantPerms = []nosql.PrefixPermissions{ |
| 240 | nosql.PrefixPermissions{Prefix: nosql.Prefix("prefix"), Perms: aAndB}, |
| 241 | nosql.PrefixPermissions{Prefix: nosql.Prefix(""), Perms: aAndB}, |
| 242 | } |
| 243 | if got, _ := tb.GetPermissions(clientACtx, "prefix"); !reflect.DeepEqual(got, wantPerms) { |
| 244 | t.Fatalf("Unexpected permissions: got %v, want %v", got, wantPerms) |
| 245 | } |
| 246 | if got, _ := tb.GetPermissions(clientACtx, "prefix_c"); !reflect.DeepEqual(got, wantPerms) { |
| 247 | t.Fatalf("Unexpected permissions: got %v, want %v", got, wantPerms) |
| 248 | } |
| 249 | wantPerms = []nosql.PrefixPermissions{ |
| 250 | nosql.PrefixPermissions{Prefix: nosql.Prefix("prefix_a"), Perms: aOnly}, |
| 251 | nosql.PrefixPermissions{Prefix: nosql.Prefix("prefix"), Perms: aAndB}, |
| 252 | nosql.PrefixPermissions{Prefix: nosql.Prefix(""), Perms: aAndB}, |
| 253 | } |
| 254 | if got, _ := tb.GetPermissions(clientACtx, "prefix_a"); !reflect.DeepEqual(got, wantPerms) { |
| 255 | t.Fatalf("Unexpected permissions: got %v, want %v", got, wantPerms) |
| 256 | } |
| 257 | if got, _ := tb.GetPermissions(clientACtx, "prefix_a_suffix"); !reflect.DeepEqual(got, wantPerms) { |
| 258 | t.Fatalf("Unexpected permissions: got %v, want %v", got, wantPerms) |
| 259 | } |
| 260 | wantPerms = []nosql.PrefixPermissions{ |
| 261 | nosql.PrefixPermissions{Prefix: nosql.Prefix("prefix_b"), Perms: bOnly}, |
| 262 | nosql.PrefixPermissions{Prefix: nosql.Prefix("prefix"), Perms: aAndB}, |
| 263 | nosql.PrefixPermissions{Prefix: nosql.Prefix(""), Perms: aAndB}, |
| 264 | } |
| 265 | if got, _ := tb.GetPermissions(clientACtx, "prefix_b"); !reflect.DeepEqual(got, wantPerms) { |
| 266 | t.Fatalf("Unexpected permissions: got %v, want %v", got, wantPerms) |
| 267 | } |
| 268 | if got, _ := tb.GetPermissions(clientACtx, "prefix_b_suffix"); !reflect.DeepEqual(got, wantPerms) { |
| 269 | t.Fatalf("Unexpected permissions: got %v, want %v", got, wantPerms) |
| 270 | } |
| 271 | |
| 272 | // Delete some prefix permissions and check again. |
| 273 | // Check that A can't delete permissions of B. |
| 274 | if err := tb.DeletePermissions(clientACtx, nosql.Prefix("prefix_b")); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 275 | t.Fatalf("tb.DeletePermissions() should have failed: %v", err) |
| 276 | } |
| 277 | if err := tb.DeletePermissions(clientBCtx, nosql.Prefix("prefix_a")); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 278 | t.Fatalf("tb.DeletePermissions() should have failed: %v", err) |
| 279 | } |
| 280 | // Delete 'prefix' and 'prefix_a' |
| 281 | if err := tb.DeletePermissions(clientACtx, nosql.Prefix("prefix")); err != nil { |
| 282 | t.Fatalf("tb.DeletePermissions() failed: %v", err) |
| 283 | } |
| 284 | if err := tb.DeletePermissions(clientACtx, nosql.Prefix("prefix_a")); err != nil { |
| 285 | t.Fatalf("tb.DeletePermissions() failed: %v", err) |
| 286 | } |
| 287 | // Check DeletePermissions is idempotent. |
| 288 | if err := tb.DeletePermissions(clientACtx, nosql.Prefix("prefix")); err != nil { |
| 289 | t.Fatalf("tb.DeletePermissions() failed: %v", err) |
| 290 | } |
| 291 | |
| 292 | // Check GetPermissions again. |
| 293 | wantPerms = []nosql.PrefixPermissions{ |
| 294 | nosql.PrefixPermissions{Prefix: nosql.Prefix(""), Perms: aAndB}, |
| 295 | } |
| 296 | if got, _ := tb.GetPermissions(clientACtx, ""); !reflect.DeepEqual(got, wantPerms) { |
| 297 | t.Fatalf("Unexpected permissions: got %v, want %v", got, wantPerms) |
| 298 | } |
| 299 | if got, _ := tb.GetPermissions(clientACtx, "prefix"); !reflect.DeepEqual(got, wantPerms) { |
| 300 | t.Fatalf("Unexpected permissions: got %v, want %v", got, wantPerms) |
| 301 | } |
| 302 | if got, _ := tb.GetPermissions(clientACtx, "prefix_a"); !reflect.DeepEqual(got, wantPerms) { |
| 303 | t.Fatalf("Unexpected permissions: got %v, want %v", got, wantPerms) |
| 304 | } |
| 305 | if got, _ := tb.GetPermissions(clientACtx, "prefix_a_suffix"); !reflect.DeepEqual(got, wantPerms) { |
| 306 | t.Fatalf("Unexpected permissions: got %v, want %v", got, wantPerms) |
| 307 | } |
| 308 | wantPerms = []nosql.PrefixPermissions{ |
| 309 | nosql.PrefixPermissions{Prefix: nosql.Prefix("prefix_b"), Perms: bOnly}, |
| 310 | nosql.PrefixPermissions{Prefix: nosql.Prefix(""), Perms: aAndB}, |
| 311 | } |
| 312 | if got, _ := tb.GetPermissions(clientACtx, "prefix_b"); !reflect.DeepEqual(got, wantPerms) { |
| 313 | t.Fatalf("Unexpected permissions: got %v, want %v", got, wantPerms) |
| 314 | } |
| 315 | if got, _ := tb.GetPermissions(clientACtx, "prefix_b_suffix"); !reflect.DeepEqual(got, wantPerms) { |
| 316 | t.Fatalf("Unexpected permissions: got %v, want %v", got, wantPerms) |
| 317 | } |
| 318 | |
| 319 | // Remove B from table-level permissions and check B has no access. |
| 320 | if err := tb.SetPermissions(clientACtx, nosql.Prefix(""), aOnly); err != nil { |
| 321 | t.Fatalf("tb.SetPermissions() failed: %v", err) |
| 322 | } |
| 323 | if _, err := tb.GetPermissions(clientBCtx, ""); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 324 | t.Fatalf("tb.GetPermissions() should have failed: %v", err) |
| 325 | } |
| 326 | if _, err := tb.GetPermissions(clientBCtx, "prefix_b"); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 327 | t.Fatalf("tb.GetPermissions() should have failed: %v", err) |
| 328 | } |
| 329 | if err := tb.SetPermissions(clientBCtx, nosql.Prefix(""), bOnly); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 330 | t.Fatalf("tb.SetPermissions() should have failed: %v", err) |
| 331 | } |
| 332 | if err := tb.SetPermissions(clientBCtx, nosql.Prefix("prefix_b"), bOnly); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 333 | t.Fatalf("tb.SetPermissions() should have failed: %v", err) |
| 334 | } |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 335 | } |
| 336 | |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 337 | //////////////////////////////////////// |
| 338 | // Tests involving rows |
| 339 | |
| 340 | type Foo struct { |
| 341 | I int |
| 342 | S string |
| 343 | } |
| 344 | |
| 345 | type Bar struct { |
| 346 | F float32 |
| 347 | S string |
| 348 | } |
| 349 | |
John Kline | 4894fb1 | 2015-06-17 09:06:56 -0700 | [diff] [blame] | 350 | type Baz struct { |
| 351 | Name string |
| 352 | Active bool |
| 353 | } |
| 354 | |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 355 | // Tests that Table.Scan works as expected. |
| 356 | func TestTableScan(t *testing.T) { |
| 357 | ctx, sName, cleanup := tu.SetupOrDie(nil) |
| 358 | defer cleanup() |
| 359 | a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a") |
| 360 | d := tu.CreateNoSQLDatabase(t, ctx, a, "d") |
| 361 | tb := tu.CreateTable(t, ctx, d, "tb") |
| 362 | |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 363 | tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{}, []interface{}{}) |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 364 | |
| 365 | fooWant := Foo{I: 4, S: "f"} |
| 366 | if err := tb.Put(ctx, "foo", &fooWant); err != nil { |
| 367 | t.Fatalf("tb.Put() failed: %v", err) |
| 368 | } |
| 369 | barWant := Bar{F: 0.5, S: "b"} |
| 370 | if err := tb.Put(ctx, "bar", &barWant); err != nil { |
| 371 | t.Fatalf("tb.Put() failed: %v", err) |
| 372 | } |
| 373 | |
| 374 | // Match all keys. |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 375 | tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{"bar", "foo"}, []interface{}{&barWant, &fooWant}) |
| 376 | tu.CheckScan(t, ctx, tb, nosql.Range("", ""), []string{"bar", "foo"}, []interface{}{&barWant, &fooWant}) |
| 377 | tu.CheckScan(t, ctx, tb, nosql.Range("", "z"), []string{"bar", "foo"}, []interface{}{&barWant, &fooWant}) |
| 378 | tu.CheckScan(t, ctx, tb, nosql.Range("a", ""), []string{"bar", "foo"}, []interface{}{&barWant, &fooWant}) |
| 379 | tu.CheckScan(t, ctx, tb, nosql.Range("a", "z"), []string{"bar", "foo"}, []interface{}{&barWant, &fooWant}) |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 380 | |
| 381 | // Match "bar" only. |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 382 | tu.CheckScan(t, ctx, tb, nosql.Prefix("b"), []string{"bar"}, []interface{}{&barWant}) |
| 383 | tu.CheckScan(t, ctx, tb, nosql.Prefix("bar"), []string{"bar"}, []interface{}{&barWant}) |
| 384 | tu.CheckScan(t, ctx, tb, nosql.Range("bar", "baz"), []string{"bar"}, []interface{}{&barWant}) |
| 385 | tu.CheckScan(t, ctx, tb, nosql.Range("bar", "foo"), []string{"bar"}, []interface{}{&barWant}) |
| 386 | tu.CheckScan(t, ctx, tb, nosql.Range("", "foo"), []string{"bar"}, []interface{}{&barWant}) |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 387 | |
| 388 | // Match "foo" only. |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 389 | tu.CheckScan(t, ctx, tb, nosql.Prefix("f"), []string{"foo"}, []interface{}{&fooWant}) |
| 390 | tu.CheckScan(t, ctx, tb, nosql.Prefix("foo"), []string{"foo"}, []interface{}{&fooWant}) |
| 391 | tu.CheckScan(t, ctx, tb, nosql.Range("foo", "fox"), []string{"foo"}, []interface{}{&fooWant}) |
| 392 | tu.CheckScan(t, ctx, tb, nosql.Range("foo", ""), []string{"foo"}, []interface{}{&fooWant}) |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 393 | |
| 394 | // Match nothing. |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 395 | tu.CheckScan(t, ctx, tb, nosql.Range("a", "bar"), []string{}, []interface{}{}) |
| 396 | tu.CheckScan(t, ctx, tb, nosql.Range("bar", "bar"), []string{}, []interface{}{}) |
| 397 | tu.CheckScan(t, ctx, tb, nosql.Prefix("z"), []string{}, []interface{}{}) |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 398 | } |
| 399 | |
Adam Sadovsky | c5cc4a3 | 2015-06-02 18:44:46 -0700 | [diff] [blame] | 400 | // Tests that Table.Delete works as expected. |
| 401 | func TestTableDeleteRowRange(t *testing.T) { |
| 402 | ctx, sName, cleanup := tu.SetupOrDie(nil) |
| 403 | defer cleanup() |
| 404 | a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a") |
| 405 | d := tu.CreateNoSQLDatabase(t, ctx, a, "d") |
| 406 | tb := tu.CreateTable(t, ctx, d, "tb") |
| 407 | |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 408 | tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{}, []interface{}{}) |
Adam Sadovsky | c5cc4a3 | 2015-06-02 18:44:46 -0700 | [diff] [blame] | 409 | |
| 410 | // Put foo and bar. |
| 411 | fooWant := Foo{I: 4, S: "f"} |
| 412 | if err := tb.Put(ctx, "foo", &fooWant); err != nil { |
| 413 | t.Fatalf("tb.Put() failed: %v", err) |
| 414 | } |
| 415 | barWant := Bar{F: 0.5, S: "b"} |
| 416 | if err := tb.Put(ctx, "bar", &barWant); err != nil { |
| 417 | t.Fatalf("tb.Put() failed: %v", err) |
| 418 | } |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 419 | tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{"bar", "foo"}, []interface{}{&barWant, &fooWant}) |
Adam Sadovsky | c5cc4a3 | 2015-06-02 18:44:46 -0700 | [diff] [blame] | 420 | |
| 421 | // Delete foo. |
| 422 | if err := tb.Delete(ctx, nosql.Prefix("f")); err != nil { |
| 423 | t.Fatalf("tb.Delete() failed: %v", err) |
| 424 | } |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 425 | tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{"bar"}, []interface{}{&barWant}) |
Adam Sadovsky | c5cc4a3 | 2015-06-02 18:44:46 -0700 | [diff] [blame] | 426 | |
| 427 | // Restore foo. |
| 428 | if err := tb.Put(ctx, "foo", &fooWant); err != nil { |
| 429 | t.Fatalf("tb.Put() failed: %v", err) |
| 430 | } |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 431 | tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{"bar", "foo"}, []interface{}{&barWant, &fooWant}) |
Adam Sadovsky | c5cc4a3 | 2015-06-02 18:44:46 -0700 | [diff] [blame] | 432 | |
| 433 | // Delete everything. |
| 434 | if err := tb.Delete(ctx, nosql.Prefix("")); err != nil { |
| 435 | t.Fatalf("tb.Delete() failed: %v", err) |
| 436 | } |
Adam Sadovsky | fd0cd95 | 2015-06-04 22:23:51 -0700 | [diff] [blame] | 437 | tu.CheckScan(t, ctx, tb, nosql.Prefix(""), []string{}, []interface{}{}) |
Adam Sadovsky | c5cc4a3 | 2015-06-02 18:44:46 -0700 | [diff] [blame] | 438 | } |
| 439 | |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 440 | // Tests that Table.{Get,Put,Delete} work as expected. |
| 441 | func TestTableRowMethods(t *testing.T) { |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 442 | ctx, sName, cleanup := tu.SetupOrDie(nil) |
| 443 | defer cleanup() |
| 444 | a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a") |
| 445 | d := tu.CreateNoSQLDatabase(t, ctx, a, "d") |
| 446 | tb := tu.CreateTable(t, ctx, d, "tb") |
| 447 | |
| 448 | got, want := Foo{}, Foo{I: 4, S: "foo"} |
| 449 | if err := tb.Get(ctx, "f", &got); verror.ErrorID(err) != verror.ErrNoExist.ID { |
| 450 | t.Fatalf("tb.Get() should have failed: %v", err) |
| 451 | } |
| 452 | if err := tb.Put(ctx, "f", &want); err != nil { |
| 453 | t.Fatalf("tb.Put() failed: %v", err) |
| 454 | } |
| 455 | if err := tb.Get(ctx, "f", &got); err != nil { |
| 456 | t.Fatalf("tb.Get() failed: %v", err) |
| 457 | } |
| 458 | if !reflect.DeepEqual(got, want) { |
| 459 | t.Fatalf("Values do not match: got %v, want %v", got, want) |
| 460 | } |
| 461 | // Overwrite value. |
| 462 | want.I = 6 |
| 463 | if err := tb.Put(ctx, "f", &want); err != nil { |
| 464 | t.Fatalf("tb.Put() failed: %v", err) |
| 465 | } |
| 466 | if err := tb.Get(ctx, "f", &got); err != nil { |
| 467 | t.Fatalf("tb.Get() failed: %v", err) |
| 468 | } |
| 469 | if !reflect.DeepEqual(got, want) { |
| 470 | t.Fatalf("Values do not match: got %v, want %v", got, want) |
| 471 | } |
Adam Sadovsky | c5cc4a3 | 2015-06-02 18:44:46 -0700 | [diff] [blame] | 472 | if err := tb.Delete(ctx, nosql.Prefix("f")); err != nil { |
| 473 | t.Fatalf("tb.Delete() failed: %v", err) |
| 474 | } |
| 475 | if err := tb.Get(ctx, "f", &got); verror.ErrorID(err) != verror.ErrNoExist.ID { |
| 476 | t.Fatalf("r.Get() should have failed: %v", err) |
| 477 | } |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 478 | } |
| 479 | |
| 480 | // Tests that Row.{Get,Put,Delete} work as expected. |
| 481 | func TestRowMethods(t *testing.T) { |
Adam Sadovsky | 13922e3 | 2015-05-19 17:39:59 -0700 | [diff] [blame] | 482 | ctx, sName, cleanup := tu.SetupOrDie(nil) |
| 483 | defer cleanup() |
| 484 | a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a") |
| 485 | d := tu.CreateNoSQLDatabase(t, ctx, a, "d") |
| 486 | tb := tu.CreateTable(t, ctx, d, "tb") |
| 487 | |
| 488 | r := tb.Row("f") |
| 489 | got, want := Foo{}, Foo{I: 4, S: "foo"} |
| 490 | if err := r.Get(ctx, &got); verror.ErrorID(err) != verror.ErrNoExist.ID { |
| 491 | t.Fatalf("r.Get() should have failed: %v", err) |
| 492 | } |
| 493 | if err := r.Put(ctx, &want); err != nil { |
| 494 | t.Fatalf("r.Put() failed: %v", err) |
| 495 | } |
| 496 | if err := r.Get(ctx, &got); err != nil { |
| 497 | t.Fatalf("r.Get() failed: %v", err) |
| 498 | } |
| 499 | if !reflect.DeepEqual(got, want) { |
| 500 | t.Fatalf("Values do not match: got %v, want %v", got, want) |
| 501 | } |
| 502 | // Overwrite value. |
| 503 | want.I = 6 |
| 504 | if err := r.Put(ctx, &want); err != nil { |
| 505 | t.Fatalf("r.Put() failed: %v", err) |
| 506 | } |
| 507 | if err := r.Get(ctx, &got); err != nil { |
| 508 | t.Fatalf("r.Get() failed: %v", err) |
| 509 | } |
| 510 | if !reflect.DeepEqual(got, want) { |
| 511 | t.Fatalf("Values do not match: got %v, want %v", got, want) |
| 512 | } |
| 513 | if err := r.Delete(ctx); err != nil { |
| 514 | t.Fatalf("r.Delete() failed: %v", err) |
| 515 | } |
| 516 | if err := r.Get(ctx, &got); verror.ErrorID(err) != verror.ErrNoExist.ID { |
| 517 | t.Fatalf("r.Get() should have failed: %v", err) |
| 518 | } |
Adam Sadovsky | aba9d50 | 2015-04-10 22:06:06 -0700 | [diff] [blame] | 519 | } |
Sergey Rogulenko | 445e041 | 2015-06-18 23:06:11 -0700 | [diff] [blame] | 520 | |
| 521 | // Test permission checking in Row.{Get,Put,Delete} and |
| 522 | // Table.{Scan, DeleteRowRange}. |
| 523 | func TestRowPermissions(t *testing.T) { |
Adam Sadovsky | da06920 | 2015-06-24 12:56:11 -0700 | [diff] [blame] | 524 | ctx, clientACtx, sName, rootp, cleanup := tu.SetupOrDieCustom("clientA", "server", nil) |
Sergey Rogulenko | 445e041 | 2015-06-18 23:06:11 -0700 | [diff] [blame] | 525 | defer cleanup() |
Adam Sadovsky | da06920 | 2015-06-24 12:56:11 -0700 | [diff] [blame] | 526 | clientBCtx := tu.NewCtx(ctx, rootp, "clientB") |
Sergey Rogulenko | 445e041 | 2015-06-18 23:06:11 -0700 | [diff] [blame] | 527 | a := tu.CreateApp(t, clientACtx, syncbase.NewService(sName), "a") |
| 528 | d := tu.CreateNoSQLDatabase(t, clientACtx, a, "d") |
| 529 | tb := tu.CreateTable(t, clientACtx, d, "tb") |
| 530 | |
| 531 | // Permission objects. |
Adam Sadovsky | da06920 | 2015-06-24 12:56:11 -0700 | [diff] [blame] | 532 | aAndB := tu.DefaultPerms("root/clientA", "root/clientB") |
| 533 | aOnly := tu.DefaultPerms("root/clientA") |
| 534 | bOnly := tu.DefaultPerms("root/clientB") |
Sergey Rogulenko | 445e041 | 2015-06-18 23:06:11 -0700 | [diff] [blame] | 535 | |
| 536 | // Set initial permissions. |
| 537 | if err := tb.SetPermissions(clientACtx, nosql.Prefix(""), aAndB); err != nil { |
| 538 | t.Fatalf("tb.SetPermissions() failed: %v", err) |
| 539 | } |
| 540 | if err := tb.SetPermissions(clientACtx, nosql.Prefix("a"), aOnly); err != nil { |
| 541 | t.Fatalf("tb.SetPermissions() failed: %v", err) |
| 542 | } |
| 543 | if err := tb.SetPermissions(clientBCtx, nosql.Prefix("b"), bOnly); err != nil { |
| 544 | t.Fatalf("tb.SetPermissions() failed: %v", err) |
| 545 | } |
| 546 | |
| 547 | // Add some key-value pairs. |
| 548 | ra := tb.Row("afoo") |
| 549 | rb := tb.Row("bfoo") |
| 550 | if err := ra.Put(clientACtx, Foo{}); err != nil { |
| 551 | t.Fatalf("ra.Put() failed: %v", err) |
| 552 | } |
| 553 | if err := rb.Put(clientBCtx, Foo{}); err != nil { |
| 554 | t.Fatalf("rb.Put() failed: %v", err) |
| 555 | } |
| 556 | |
| 557 | // Check A doesn't have access to 'b'. |
| 558 | if err := rb.Get(clientACtx, &Foo{}); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 559 | t.Fatalf("rb.Get() should have failed: %v", err) |
| 560 | } |
| 561 | if err := rb.Put(clientACtx, Foo{}); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 562 | t.Fatalf("rb.Put() should have failed: %v", err) |
| 563 | } |
| 564 | if err := rb.Delete(clientACtx); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 565 | t.Fatalf("rb.Delete() should have failed: %v", err) |
| 566 | } |
| 567 | // Test Table.Delete and Scan. |
| 568 | if err := tb.Delete(clientACtx, nosql.Prefix("")); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 569 | t.Fatalf("tb.Delete should have failed: %v", err) |
| 570 | } |
| 571 | s := tb.Scan(clientACtx, nosql.Prefix("")) |
| 572 | if !s.Advance() { |
| 573 | t.Fatalf("Stream should have advanced: %v", s.Err()) |
| 574 | } |
| 575 | if s.Key() != "afoo" { |
| 576 | t.Fatalf("Unexpected key: got %q, want %q", s.Key(), "afoo") |
| 577 | } |
| 578 | if s.Advance() { |
| 579 | t.Fatalf("Stream advanced unexpectedly") |
| 580 | } |
| 581 | if err := s.Err(); verror.ErrorID(err) != verror.ErrNoAccess.ID { |
| 582 | t.Fatalf("Unexpected stream error: %v", err) |
| 583 | } |
| 584 | } |
Sergey Rogulenko | 1f988de | 2015-08-14 17:00:09 -0700 | [diff] [blame] | 585 | |
| 586 | // TestWatchBasic test the basic client watch functionality: no perms, |
| 587 | // no batches. |
| 588 | func TestWatchBasic(t *testing.T) { |
| 589 | ctx, sName, cleanup := tu.SetupOrDie(nil) |
| 590 | defer cleanup() |
| 591 | a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a") |
| 592 | d := tu.CreateNoSQLDatabase(t, ctx, a, "d") |
| 593 | tb := tu.CreateTable(t, ctx, d, "tb") |
| 594 | var resumeMarkers []watch.ResumeMarker |
| 595 | |
| 596 | // Generate the data and resume markers. |
| 597 | // Initial state. |
| 598 | resumeMarker, err := d.GetResumeMarker(ctx) |
| 599 | if err != nil { |
| 600 | t.Fatalf("d.GetResumeMarker() failed: %v", err) |
| 601 | } |
| 602 | resumeMarkers = append(resumeMarkers, resumeMarker) |
| 603 | // Put "abc". |
| 604 | r := tb.Row("abc") |
| 605 | if err := r.Put(ctx, "value"); err != nil { |
| 606 | t.Fatalf("r.Put() failed: %v", err) |
| 607 | } |
| 608 | if resumeMarker, err = d.GetResumeMarker(ctx); err != nil { |
| 609 | t.Fatalf("d.GetResumeMarker() failed: %v", err) |
| 610 | } |
| 611 | resumeMarkers = append(resumeMarkers, resumeMarker) |
| 612 | // Delete "abc". |
| 613 | if err := r.Delete(ctx); err != nil { |
| 614 | t.Fatalf("r.Delete() failed: %v", err) |
| 615 | } |
| 616 | if resumeMarker, err = d.GetResumeMarker(ctx); err != nil { |
| 617 | t.Fatalf("d.GetResumeMarker() failed: %v", err) |
| 618 | } |
| 619 | resumeMarkers = append(resumeMarkers, resumeMarker) |
| 620 | // Put "a". |
| 621 | r = tb.Row("a") |
| 622 | if err := r.Put(ctx, "value"); err != nil { |
| 623 | t.Fatalf("r.Put() failed: %v", err) |
| 624 | } |
| 625 | if resumeMarker, err = d.GetResumeMarker(ctx); err != nil { |
| 626 | t.Fatalf("d.GetResumeMarker() failed: %v", err) |
| 627 | } |
| 628 | resumeMarkers = append(resumeMarkers, resumeMarker) |
| 629 | |
| 630 | vomValue, _ := vom.Encode("value") |
| 631 | allChanges := []nosql.WatchChange{ |
| 632 | nosql.WatchChange{ |
| 633 | Table: "tb", |
| 634 | Row: "abc", |
| 635 | ChangeType: nosql.PutChange, |
| 636 | ValueBytes: vomValue, |
| 637 | ResumeMarker: resumeMarkers[1], |
| 638 | }, |
| 639 | nosql.WatchChange{ |
| 640 | Table: "tb", |
| 641 | Row: "abc", |
| 642 | ChangeType: nosql.DeleteChange, |
| 643 | ResumeMarker: resumeMarkers[2], |
| 644 | }, |
| 645 | nosql.WatchChange{ |
| 646 | Table: "tb", |
| 647 | Row: "a", |
| 648 | ChangeType: nosql.PutChange, |
| 649 | ValueBytes: vomValue, |
| 650 | ResumeMarker: resumeMarkers[3], |
| 651 | }, |
| 652 | } |
| 653 | ctxWithTimeout, _ := context.WithTimeout(ctx, 10*time.Second) |
| 654 | wstream, _ := d.Watch(ctxWithTimeout, "tb", "a", resumeMarkers[0]) |
| 655 | tu.CheckWatch(t, wstream, allChanges) |
| 656 | wstream, _ = d.Watch(ctxWithTimeout, "tb", "a", resumeMarkers[1]) |
| 657 | tu.CheckWatch(t, wstream, allChanges[1:]) |
| 658 | wstream, _ = d.Watch(ctxWithTimeout, "tb", "a", resumeMarkers[2]) |
| 659 | tu.CheckWatch(t, wstream, allChanges[2:]) |
| 660 | |
| 661 | wstream, _ = d.Watch(ctxWithTimeout, "tb", "abc", resumeMarkers[0]) |
| 662 | tu.CheckWatch(t, wstream, allChanges[:2]) |
| 663 | wstream, _ = d.Watch(ctxWithTimeout, "tb", "abc", resumeMarkers[1]) |
| 664 | tu.CheckWatch(t, wstream, allChanges[1:2]) |
| 665 | } |
| 666 | |
| 667 | // TestWatchWithBatchAndPerms test that the client watch correctly handles |
| 668 | // batches and prefix perms. |
| 669 | func TestWatchWithBatchAndPerms(t *testing.T) { |
| 670 | ctx, clientACtx, sName, rootp, cleanup := tu.SetupOrDieCustom("clientA", "server", nil) |
| 671 | defer cleanup() |
| 672 | clientBCtx := tu.NewCtx(ctx, rootp, "clientB") |
| 673 | a := tu.CreateApp(t, clientACtx, syncbase.NewService(sName), "a") |
| 674 | d := tu.CreateNoSQLDatabase(t, clientACtx, a, "d") |
| 675 | tb := tu.CreateTable(t, clientACtx, d, "tb") |
| 676 | |
| 677 | // Set initial permissions. |
| 678 | aAndB := tu.DefaultPerms("root/clientA", "root/clientB") |
| 679 | aOnly := tu.DefaultPerms("root/clientA") |
| 680 | if err := tb.SetPermissions(clientACtx, nosql.Prefix(""), aAndB); err != nil { |
| 681 | t.Fatalf("tb.SetPermissions() failed: %v", err) |
| 682 | } |
| 683 | if err := tb.SetPermissions(clientACtx, nosql.Prefix("a"), aOnly); err != nil { |
| 684 | t.Fatalf("tb.SetPermissions() failed: %v", err) |
| 685 | } |
| 686 | // Get the initial resume marker. |
| 687 | resumeMarker, err := d.GetResumeMarker(clientACtx) |
| 688 | if err != nil { |
| 689 | t.Fatalf("d.GetResumeMarker() failed: %v", err) |
| 690 | } |
| 691 | initMarker := resumeMarker |
| 692 | // Do two puts in a batch. |
| 693 | if err := nosql.RunInBatch(clientACtx, d, wire.BatchOptions{}, func(b nosql.BatchDatabase) error { |
| 694 | tb := b.Table("tb") |
| 695 | if err := tb.Put(clientACtx, "a", "value"); err != nil { |
| 696 | return err |
| 697 | } |
| 698 | return tb.Put(clientACtx, "b", "value") |
| 699 | }); err != nil { |
| 700 | t.Fatalf("RunInBatch failed: %v", err) |
| 701 | } |
| 702 | |
| 703 | if resumeMarker, err = d.GetResumeMarker(clientACtx); err != nil { |
| 704 | t.Fatalf("d.GetResumeMarker() failed: %v", err) |
| 705 | } |
| 706 | vomValue, _ := vom.Encode("value") |
| 707 | allChanges := []nosql.WatchChange{ |
| 708 | nosql.WatchChange{ |
| 709 | Table: "tb", |
| 710 | Row: "a", |
| 711 | ChangeType: nosql.PutChange, |
| 712 | ValueBytes: vomValue, |
| 713 | ResumeMarker: nil, |
| 714 | Continued: true, |
| 715 | }, |
| 716 | nosql.WatchChange{ |
| 717 | Table: "tb", |
| 718 | Row: "b", |
| 719 | ChangeType: nosql.PutChange, |
| 720 | ValueBytes: vomValue, |
| 721 | ResumeMarker: resumeMarker, |
| 722 | }, |
| 723 | } |
| 724 | |
| 725 | ctxAWithTimeout, _ := context.WithTimeout(clientACtx, 10*time.Second) |
| 726 | ctxBWithTimeout, _ := context.WithTimeout(clientBCtx, 10*time.Second) |
| 727 | // ClientA should see both changes as one batch. |
| 728 | wstream, _ := d.Watch(ctxAWithTimeout, "tb", "", initMarker) |
| 729 | tu.CheckWatch(t, wstream, allChanges) |
| 730 | // ClientB should see only one change. |
| 731 | wstream, _ = d.Watch(ctxBWithTimeout, "tb", "", initMarker) |
| 732 | tu.CheckWatch(t, wstream, allChanges[1:]) |
| 733 | } |
| 734 | |
| 735 | // TestBlockingWatch tests that the server side of the client watch correctly |
| 736 | // blocks until new updates to the database arrive. |
| 737 | func TestBlockingWatch(t *testing.T) { |
| 738 | ctx, sName, cleanup := tu.SetupOrDie(nil) |
| 739 | defer cleanup() |
| 740 | a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a") |
| 741 | d := tu.CreateNoSQLDatabase(t, ctx, a, "d") |
| 742 | tb := tu.CreateTable(t, ctx, d, "tb") |
| 743 | |
| 744 | resumeMarker, err := d.GetResumeMarker(ctx) |
| 745 | if err != nil { |
| 746 | t.Fatalf("d.GetResumeMarker() failed: %v", err) |
| 747 | } |
| 748 | ctxWithTimeout, _ := context.WithTimeout(ctx, 10*time.Second) |
| 749 | wstream, _ := d.Watch(ctxWithTimeout, "tb", "a", resumeMarker) |
| 750 | vomValue, _ := vom.Encode("value") |
| 751 | for i := 0; i < 10; i++ { |
| 752 | // Put "abc". |
| 753 | r := tb.Row("abc") |
| 754 | if err := r.Put(ctx, "value"); err != nil { |
| 755 | t.Fatalf("r.Put() failed: %v", err) |
| 756 | } |
| 757 | if resumeMarker, err = d.GetResumeMarker(ctx); err != nil { |
| 758 | t.Fatalf("d.GetResumeMarker() failed: %v", err) |
| 759 | } |
| 760 | if !wstream.Advance() { |
| 761 | t.Fatalf("wstream.Advance() reached the end: %v", wstream.Err()) |
| 762 | } |
| 763 | want := nosql.WatchChange{ |
| 764 | Table: "tb", |
| 765 | Row: "abc", |
| 766 | ChangeType: nosql.PutChange, |
| 767 | ValueBytes: vomValue, |
| 768 | ResumeMarker: resumeMarker, |
| 769 | } |
| 770 | if got := wstream.Change(); !reflect.DeepEqual(got, want) { |
| 771 | t.Fatalf("unexpected watch change: got %v, want %v", got, want) |
| 772 | } |
| 773 | } |
| 774 | } |
| 775 | |
| 776 | // TestBlockedWatchCancel tests that the watch call blocked on the server side |
| 777 | // can be successfully canceled from the client. |
| 778 | func TestBlockedWatchCancel(t *testing.T) { |
| 779 | ctx, sName, cleanup := tu.SetupOrDie(nil) |
| 780 | defer cleanup() |
| 781 | a := tu.CreateApp(t, ctx, syncbase.NewService(sName), "a") |
| 782 | d := tu.CreateNoSQLDatabase(t, ctx, a, "d") |
| 783 | |
| 784 | resumeMarker, err := d.GetResumeMarker(ctx) |
| 785 | if err != nil { |
| 786 | t.Fatalf("d.GetResumeMarker() failed: %v", err) |
| 787 | } |
| 788 | ctxWithTimeout, _ := context.WithTimeout(ctx, 100*time.Millisecond) |
| 789 | wstream, _ := d.Watch(ctxWithTimeout, "tb", "a", resumeMarker) |
| 790 | if wstream.Advance() { |
| 791 | t.Fatalf("wstream advanced") |
| 792 | } |
| 793 | if got, want := verror.ErrorID(wstream.Err()), verror.ErrTimeout.ID; got != want { |
| 794 | t.Fatalf("unexpected wstream error ID: got %v, want %v", got, want) |
| 795 | } |
| 796 | } |