diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e1a8ef567bb5b16fa78d4bf6b5537c5103d45dc9..b145fc985b6ea48f9ef9ba9d06c0ba082b0f8955 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,6 +25,8 @@ stages: build: stage: build + except: + - tags script: - go mod vendor -v - mkdir -p release @@ -36,6 +38,8 @@ build: wasm-test: stage: test + except: + - tags script: - export PATH=/root/go/bin:$PATH - echo > utils/utils_js.s @@ -44,23 +48,29 @@ wasm-test: go-test: stage: test + except: + - tags script: - go mod vendor -v - go test ./... -v version_check: stage: version_check + except: + - tags only: - master - release image: $DOCKER_IMAGE script: - GITTAG=$(git describe --tags) - - CODEVERS=$(cat utils/version.go | grep "const SEMVER =" | cut -d ' ' -f4 | tr -d '"') + - CODEVERS=$(cat storage/version.go | grep "const SEMVER =" | cut -d ' ' -f4 | tr -d '"') - if [[ $GITTAG != $CODEVERS ]]; then echo "VERSION NUMBER BAD $GITTAG != $CODEVER"; exit -1; fi tag: stage: build + except: + - tags image: $DOCKER_IMAGE script: - git remote add origin_tags git@$GITLAB_SERVER:elixxir/xxdk-wasm.git || true @@ -76,6 +86,8 @@ tag: # master/release, this will fail to pull the latest client, and the docs will not update. doc-update: stage: doc-update + except: + - tags image: $DOCKER_IMAGE script: # We use GOPRIVATE blank because not want to directly pull client, we want to use the public cache. diff --git a/indexedDb/channels/implementation.go b/indexedDb/channels/implementation.go index 36c5980921de39d694c1ab0b0df3ecb702bb439c..bcd957f13863d47e2ce493f6c00b5e5bfd4775b4 100644 --- a/indexedDb/channels/implementation.go +++ b/indexedDb/channels/implementation.go @@ -288,15 +288,20 @@ func (w *wasmModel) UpdateSentStatus(uuid uint64, // Use the key to get the existing Message currentMsg, err := indexedDb.Get(w.db, messageStoreName, key) if err != nil { + jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr, + "Unable to get message: %+v", err)) return } // Extract the existing Message and update the Status newMessage := &Message{} - err = json.Unmarshal([]byte(currentMsg), newMessage) + err = json.Unmarshal([]byte(utils.JsToJson(currentMsg)), newMessage) if err != nil { + jww.ERROR.Printf("%+v", errors.WithMessagef(parentErr, + "Could not JSON unmarshal message: %+v", err)) return } + newMessage.Status = uint8(status) if !messageID.Equals(cryptoChannel.MessageID{}) { newMessage.MessageID = messageID.Bytes() diff --git a/indexedDb/channels/implementation_test.go b/indexedDb/channels/implementation_test.go index a14de4f928575189915a04d84a634d160e44d1d8..89a2d60835a80e2d4fd0a29b5ecde8dcc71d59ab 100644 --- a/indexedDb/channels/implementation_test.go +++ b/indexedDb/channels/implementation_test.go @@ -36,8 +36,36 @@ func TestMain(m *testing.M) { func dummyCallback(uint64, *id.ID, bool) {} +// Happy path, insert message and look it up +func TestWasmModel_msgIDLookup(t *testing.T) { + storage.GetLocalStorage().Clear() + testString := "test" + testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1}) + eventModel, err := newWASMModel(testString, nil, dummyCallback) + if err != nil { + t.Fatalf("%+v", err) + } + + testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil, + testString, []byte(testString), []byte{8, 6, 7, 5}, 0, netTime.Now(), + time.Second, 0, 0, channels.Sent) + _, err = eventModel.receiveHelper(testMsg, false) + if err != nil { + t.Fatalf("%+v", err) + } + + uuid, err := eventModel.msgIDLookup(testMsgId) + if err != nil { + t.Fatalf("%+v", err) + } + if uuid == 0 { + t.Fatalf("Expected to get a UUID!") + } +} + // Test wasmModel.UpdateSentStatus happy path and ensure fields don't change. func Test_wasmModel_UpdateSentStatus(t *testing.T) { + storage.GetLocalStorage().Clear() testString := "test" testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1}) eventModel, err := newWASMModel(testString, nil, dummyCallback) @@ -132,6 +160,7 @@ func Test_wasmModel_JoinChannel_LeaveChannel(t *testing.T) { // Test UUID gets returned when different messages are added. func Test_wasmModel_UUIDTest(t *testing.T) { + storage.GetLocalStorage().Clear() testString := "testHello" eventModel, err := newWASMModel(testString, nil, dummyCallback) if err != nil { @@ -198,6 +227,7 @@ func Test_wasmModel_DuplicateReceives(t *testing.T) { // Happy path: Inserts many messages, deletes some, and checks that the final // result is as expected. func Test_wasmModel_deleteMsgByChannel(t *testing.T) { + storage.GetLocalStorage().Clear() testString := "test_deleteMsgByChannel" totalMessages := 10 expectedMessages := 5 @@ -254,6 +284,7 @@ func Test_wasmModel_deleteMsgByChannel(t *testing.T) { // This test is designed to prove the behavior of unique indexes. // Inserts will not fail, they simply will not happen. func TestWasmModel_receiveHelper_UniqueIndex(t *testing.T) { + storage.GetLocalStorage().Clear() testString := "test_receiveHelper_UniqueIndex" eventModel, err := newWASMModel(testString, nil, dummyCallback) if err != nil { diff --git a/indexedDb/channels/init.go b/indexedDb/channels/init.go index 0b52434ff260ee3df42c8b0f201527f08377205b..12d6bb8840f3d75c8778b871ac883a84ad008ada 100644 --- a/indexedDb/channels/init.go +++ b/indexedDb/channels/init.go @@ -211,7 +211,7 @@ func (w *wasmModel) hackTestDb() error { if err != nil { return err } - if len(result) == 0 { + if result.IsUndefined() { return errors.Errorf("Failed to test db, record not present") } return nil diff --git a/indexedDb/utils.go b/indexedDb/utils.go index 7d7e7e6b394eb492ea09519f5939f4fd5c79ca59..1aa6ff45805c1c65b9ad27e51a8b0590e7b8201d 100644 --- a/indexedDb/utils.go +++ b/indexedDb/utils.go @@ -32,25 +32,25 @@ func NewContext() (context.Context, context.CancelFunc) { } // Get is a generic helper for getting values from the given [idb.ObjectStore]. -func Get(db *idb.Database, objectStoreName string, key js.Value) (string, error) { +func Get(db *idb.Database, objectStoreName string, key js.Value) (js.Value, error) { parentErr := errors.Errorf("failed to Get %s/%s", objectStoreName, key) // Prepare the Transaction txn, err := db.Transaction(idb.TransactionReadOnly, objectStoreName) if err != nil { - return "", errors.WithMessagef(parentErr, + return js.Undefined(), errors.WithMessagef(parentErr, "Unable to create Transaction: %+v", err) } store, err := txn.ObjectStore(objectStoreName) if err != nil { - return "", errors.WithMessagef(parentErr, + return js.Undefined(), errors.WithMessagef(parentErr, "Unable to get ObjectStore: %+v", err) } // Perform the operation getRequest, err := store.Get(key) if err != nil { - return "", errors.WithMessagef(parentErr, + return js.Undefined(), errors.WithMessagef(parentErr, "Unable to Get from ObjectStore: %+v", err) } @@ -59,14 +59,17 @@ func Get(db *idb.Database, objectStoreName string, key js.Value) (string, error) resultObj, err := getRequest.Await(ctx) cancel() if err != nil { - return "", errors.WithMessagef(parentErr, + return js.Undefined(), errors.WithMessagef(parentErr, "Unable to get from ObjectStore: %+v", err) + } else if resultObj.IsUndefined() { + return js.Undefined(), errors.WithMessage(parentErr, + "Unable to get from ObjectStore: result is undefined") } // Process result into string - resultStr := utils.JsToJson(resultObj) - jww.DEBUG.Printf("Got from %s/%s: %s", objectStoreName, key, resultStr) - return resultStr, nil + jww.DEBUG.Printf("Got from %s/%s: %s", + objectStoreName, key, utils.JsToJson(resultObj)) + return resultObj, nil } // GetIndex is a generic helper for getting values from the given @@ -79,24 +82,24 @@ func GetIndex(db *idb.Database, objectStoreName string, // Prepare the Transaction txn, err := db.Transaction(idb.TransactionReadOnly, objectStoreName) if err != nil { - return js.Value{}, errors.WithMessagef(parentErr, + return js.Undefined(), errors.WithMessagef(parentErr, "Unable to create Transaction: %+v", err) } store, err := txn.ObjectStore(objectStoreName) if err != nil { - return js.Value{}, errors.WithMessagef(parentErr, + return js.Undefined(), errors.WithMessagef(parentErr, "Unable to get ObjectStore: %+v", err) } idx, err := store.Index(indexName) if err != nil { - return js.Value{}, errors.WithMessagef(parentErr, + return js.Undefined(), errors.WithMessagef(parentErr, "Unable to get Index: %+v", err) } // Perform the operation getRequest, err := idx.Get(key) if err != nil { - return js.Value{}, errors.WithMessagef(parentErr, + return js.Undefined(), errors.WithMessagef(parentErr, "Unable to Get from ObjectStore: %+v", err) } @@ -105,14 +108,16 @@ func GetIndex(db *idb.Database, objectStoreName string, resultObj, err := getRequest.Await(ctx) cancel() if err != nil { - return js.Value{}, errors.WithMessagef(parentErr, + return js.Undefined(), errors.WithMessagef(parentErr, "Unable to get from ObjectStore: %+v", err) + } else if resultObj.IsUndefined() { + return js.Undefined(), errors.WithMessage(parentErr, + "Unable to get from ObjectStore: result is undefined") } // Process result into string - resultStr := utils.JsToJson(resultObj) jww.DEBUG.Printf("Got from %s/%s/%s: %s", - objectStoreName, indexName, key, resultStr) + objectStoreName, indexName, key, utils.JsToJson(resultObj)) return resultObj, nil } @@ -143,7 +148,7 @@ func Put(db *idb.Database, objectStoreName string, value js.Value) (*idb.Request return nil, errors.Errorf("Putting value failed: %+v", err) } jww.DEBUG.Printf("Successfully put value in %s: %v", - objectStoreName, value.String()) + objectStoreName, utils.JsToJson(value)) return request, nil } diff --git a/indexedDb/utils_test.go b/indexedDb/utils_test.go new file mode 100644 index 0000000000000000000000000000000000000000..1c657ebd5dbb4607c977323dc8883042989f05f9 --- /dev/null +++ b/indexedDb/utils_test.go @@ -0,0 +1,80 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 xx foundation // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file. // +//////////////////////////////////////////////////////////////////////////////// + +//go:build js && wasm + +package indexedDb + +import ( + "github.com/hack-pad/go-indexeddb/idb" + "strings" + "syscall/js" + "testing" +) + +// Error path: Tests that Get returns an error when trying to get a message that +// does not exist. +func TestGet_NoMessageError(t *testing.T) { + db := newTestDB("messages", "index", t) + + _, err := Get(db, "messages", js.ValueOf(5)) + if err == nil || !strings.Contains(err.Error(), "undefined") { + t.Errorf("Did not get expected error when getting a message that "+ + "does not exist: %+v", err) + } +} + +// Error path: Tests that GetIndex returns an error when trying to get a message +// that does not exist. +func TestGetIndex_NoMessageError(t *testing.T) { + db := newTestDB("messages", "index", t) + + _, err := GetIndex(db, "messages", "index", js.ValueOf(5)) + if err == nil || !strings.Contains(err.Error(), "undefined") { + t.Errorf("Did not get expected error when getting a message that "+ + "does not exist: %+v", err) + } +} + +// newTestDB creates a new idb.Database for testing. +func newTestDB(name, index string, t *testing.T) *idb.Database { + // Attempt to open database object + ctx, cancel := NewContext() + defer cancel() + openRequest, err := idb.Global().Open(ctx, "databaseName", 0, + func(db *idb.Database, _ uint, _ uint) error { + storeOpts := idb.ObjectStoreOptions{ + KeyPath: js.ValueOf("id"), + AutoIncrement: true, + } + + // Build Message ObjectStore and Indexes + messageStore, err := db.CreateObjectStore(name, storeOpts) + if err != nil { + return err + } + + _, err = messageStore.CreateIndex( + index, js.ValueOf("id"), idb.IndexOptions{}) + if err != nil { + return err + } + + return nil + }) + if err != nil { + t.Fatal(err) + } + + // Wait for database open to finish + db, err := openRequest.Await(ctx) + if err != nil { + t.Fatal(err) + } + + return db +} diff --git a/storage/version.go b/storage/version.go index 61bc23937a227f407b059b08362938c62222d558..9530aca55472b737e1905fe233f70a78689a52b5 100644 --- a/storage/version.go +++ b/storage/version.go @@ -18,7 +18,7 @@ import ( ) // SEMVER is the current semantic version of xxDK WASM. -const SEMVER = "0.1.8" +const SEMVER = "0.1.13" // Storage keys. const (