| package datastore |
| |
| import ( |
| "encoding/json" |
| "testing" |
| |
| "github.com/moby/moby/v2/daemon/libnetwork/options" |
| "gotest.tools/v3/assert" |
| is "gotest.tools/v3/assert/cmp" |
| ) |
| |
| const dummyKey = "dummy" |
| |
| // NewTestDataStore can be used by other Tests in order to use custom datastore |
| func NewTestDataStore() *Store { |
| s := NewMockStore() |
| return &Store{store: s, cache: newCache(s)} |
| } |
| |
| func TestKey(t *testing.T) { |
| sKey := Key("hello", "world") |
| const expected = "docker/network/v1.0/hello/world/" |
| assert.Check(t, is.Equal(sKey, expected)) |
| } |
| |
| func TestKVObjectFlatKey(t *testing.T) { |
| store := NewTestDataStore() |
| expected := dummyKVObject("1000", true) |
| err := store.PutObjectAtomic(expected) |
| assert.Check(t, err) |
| |
| n := dummyObject{ID: "1000"} // GetObject uses KVObject.Key() for cache lookup. |
| err = store.GetObject(&n) |
| assert.Check(t, err) |
| assert.Check(t, is.Equal(n.Name, expected.Name)) |
| } |
| |
| func TestAtomicKVObjectFlatKey(t *testing.T) { |
| store := NewTestDataStore() |
| expected := dummyKVObject("1111", true) |
| assert.Check(t, !expected.Exists()) |
| err := store.PutObjectAtomic(expected) |
| assert.Check(t, err) |
| assert.Check(t, expected.Exists()) |
| |
| // PutObjectAtomic automatically sets the Index again. Hence the following must pass. |
| |
| err = store.PutObjectAtomic(expected) |
| assert.Check(t, err, "Atomic update should succeed.") |
| |
| // Get the latest index and try PutObjectAtomic again for the same Key |
| // This must succeed as well |
| n := dummyObject{ID: "1111"} // GetObject uses KVObject.Key() for cache lookup. |
| err = store.GetObject(&n) |
| assert.Check(t, err) |
| n.ReturnValue = true |
| err = store.PutObjectAtomic(&n) |
| assert.Check(t, err) |
| |
| // Get the Object using GetObject, then set again. |
| newObj := dummyObject{ID: "1111"} // GetObject uses KVObject.Key() for cache lookup. |
| err = store.GetObject(&newObj) |
| assert.Check(t, err) |
| assert.Check(t, newObj.Exists()) |
| err = store.PutObjectAtomic(&n) |
| assert.Check(t, err) |
| } |
| |
| // dummy data used to test the datastore |
| type dummyObject struct { |
| Name string `kv:"leaf"` |
| NetworkType string `kv:"leaf"` |
| EnableIPv6 bool `kv:"leaf"` |
| Rec *recStruct `kv:"recursive"` |
| Dict map[string]*recStruct `kv:"iterative"` |
| Generic options.Generic `kv:"iterative"` |
| ID string |
| DBIndex uint64 |
| DBExists bool |
| ReturnValue bool |
| } |
| |
| func (n *dummyObject) Key() []string { |
| return []string{dummyKey, n.ID} |
| } |
| |
| func (n *dummyObject) KeyPrefix() []string { |
| return []string{dummyKey} |
| } |
| |
| func (n *dummyObject) Value() []byte { |
| if !n.ReturnValue { |
| return nil |
| } |
| |
| b, err := json.Marshal(n) |
| if err != nil { |
| return nil |
| } |
| return b |
| } |
| |
| func (n *dummyObject) SetValue(value []byte) error { |
| return json.Unmarshal(value, n) |
| } |
| |
| func (n *dummyObject) Index() uint64 { |
| return n.DBIndex |
| } |
| |
| func (n *dummyObject) SetIndex(index uint64) { |
| n.DBIndex = index |
| n.DBExists = true |
| } |
| |
| func (n *dummyObject) Exists() bool { |
| return n.DBExists |
| } |
| |
| func (n *dummyObject) Skip() bool { |
| return false |
| } |
| |
| type tmpStruct struct { |
| Name string `json:"name"` |
| NetworkType string `json:"networkType"` |
| EnableIPv6 bool `json:"enableIPv6"` |
| Generic options.Generic `json:"generic"` |
| } |
| |
| func (n *dummyObject) MarshalJSON() ([]byte, error) { |
| return json.Marshal(tmpStruct{ |
| Name: n.Name, |
| NetworkType: n.NetworkType, |
| EnableIPv6: n.EnableIPv6, |
| Generic: n.Generic, |
| }) |
| } |
| |
| func (n *dummyObject) UnmarshalJSON(b []byte) error { |
| var netMap tmpStruct |
| if err := json.Unmarshal(b, &netMap); err != nil { |
| return err |
| } |
| n.Name = netMap.Name |
| n.NetworkType = netMap.NetworkType |
| n.EnableIPv6 = netMap.EnableIPv6 |
| n.Generic = netMap.Generic |
| return nil |
| } |
| |
| func (n *dummyObject) New() KVObject { |
| return &dummyObject{} |
| } |
| |
| func (n *dummyObject) CopyTo(o KVObject) error { |
| if err := o.SetValue(n.Value()); err != nil { |
| return err |
| } |
| o.SetIndex(n.Index()) |
| return nil |
| } |
| |
| // dummy structure to test "recursive" cases |
| type recStruct struct { |
| Name string `kv:"leaf"` |
| Field1 int `kv:"leaf"` |
| Dict map[string]string `kv:"iterative"` |
| DBIndex uint64 |
| DBExists bool |
| SkipSave bool |
| } |
| |
| func (r *recStruct) Key() []string { |
| return []string{"recStruct"} |
| } |
| |
| func (r *recStruct) Value() []byte { |
| b, err := json.Marshal(r) |
| if err != nil { |
| return nil |
| } |
| return b |
| } |
| |
| func (r *recStruct) SetValue(value []byte) error { |
| return json.Unmarshal(value, r) |
| } |
| |
| func (r *recStruct) Index() uint64 { |
| return r.DBIndex |
| } |
| |
| func (r *recStruct) SetIndex(index uint64) { |
| r.DBIndex = index |
| r.DBExists = true |
| } |
| |
| func (r *recStruct) Exists() bool { |
| return r.DBExists |
| } |
| |
| func (r *recStruct) Skip() bool { |
| return r.SkipSave |
| } |
| |
| func dummyKVObject(id string, retValue bool) *dummyObject { |
| return &dummyObject{ |
| Name: "testNw", |
| NetworkType: "bridge", |
| EnableIPv6: true, |
| Rec: &recStruct{Name: "gen", Field1: 5, Dict: map[string]string{ |
| "foo": "bar", |
| "hello": "world", |
| }}, |
| ID: id, |
| DBIndex: 0, |
| ReturnValue: retValue, |
| DBExists: false, |
| Generic: options.Generic{ |
| "label1": &recStruct{Name: "value1", Field1: 1, Dict: map[string]string{ |
| "foo": "bar", |
| "hello": "world", |
| }}, |
| "label2": "subnet=10.1.1.0/16", |
| }, |
| } |
| } |