diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e1a8ef567bb5b16fa78d4bf6b5537c5103d45dc9..009f68b17dfca9079974b2997ff1ccfb530274ca 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 '"') - - if [[ $GITTAG != $CODEVERS ]]; then echo "VERSION NUMBER BAD $GITTAG != $CODEVER"; exit -1; fi + - CODEVERS=$(cat storage/version.go | grep "const SEMVER =" | cut -d ' ' -f4 | tr -d '"') + - if [[ $GITTAG != $CODEVERS ]]; then echo "VERSION NUMBER BAD $GITTAG != $CODEVERS"; 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_test.go b/indexedDb/channels/implementation_test.go index 0a7ccc77827ee9c32c3e218d67ab0a746ef59e1e..689133d3002b0567bc50545c6716d88273355601 100644 --- a/indexedDb/channels/implementation_test.go +++ b/indexedDb/channels/implementation_test.go @@ -15,6 +15,7 @@ import ( "github.com/hack-pad/go-indexeddb/idb" "gitlab.com/elixxir/xxdk-wasm/indexedDb" "gitlab.com/elixxir/xxdk-wasm/storage" + "gitlab.com/xx_network/crypto/csprng" "gitlab.com/xx_network/primitives/netTime" "os" "strconv" @@ -26,6 +27,7 @@ import ( "gitlab.com/elixxir/client/v4/cmix/rounds" cryptoBroadcast "gitlab.com/elixxir/crypto/broadcast" "gitlab.com/elixxir/crypto/channel" + cryptoChannel "gitlab.com/elixxir/crypto/channel" "gitlab.com/xx_network/primitives/id" ) @@ -38,28 +40,41 @@ 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) + cipher, err := cryptoChannel.NewCipher([]byte("testpass"), []byte("testsalt"), 128, csprng.NewSystemRNG()) if err != nil { - t.Fatalf("%+v", err) + t.Fatalf("Failed to create cipher") } + for _, c := range []cryptoChannel.Cipher{nil, cipher} { + cs := "" + if cipher != nil { + cs = "_withCipher" + } + t.Run(fmt.Sprintf("TestWasmModel_msgIDLookup%s", cs), func(t *testing.T) { + + storage.GetLocalStorage().Clear() + testString := "test" + testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1}) + eventModel, err := newWASMModel(testString, c, 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, false, false, channels.Sent) - _, err = eventModel.receiveHelper(testMsg, false) - 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, false, false, channels.Sent) + _, err = eventModel.receiveHelper(testMsg, false) + if err != nil { + t.Fatalf("%+v", err) + } - msg, err := eventModel.msgIDLookup(testMsgId) - if err != nil { - t.Fatalf("%+v", err) - } - if msg.ID == 0 { - t.Fatalf("Expected to get a UUID!") + msg, err := eventModel.msgIDLookup(testMsgId) + if err != nil { + t.Fatalf("%+v", err) + } + if msg.ID == 0 { + t.Fatalf("Expected to get a UUID!") + } + }) } } @@ -107,301 +122,374 @@ func TestWasmModel_DeleteMessage(t *testing.T) { } } -// Test wasmModel.UpdateFromUUID happy path and ensure fields don't change. -func Test_wasmModel_UpdateFromUUID(t *testing.T) { - storage.GetLocalStorage().Clear() - testString := "test" - testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1}) - eventModel, err := newWASMModel(testString, nil, dummyCallback) +// Test wasmModel.UpdateSentStatus happy path and ensure fields don't change. +func Test_wasmModel_UpdateSentStatus(t *testing.T) { + cipher, err := cryptoChannel.NewCipher([]byte("testpass"), []byte("testsalt"), 128, csprng.NewSystemRNG()) if err != nil { - t.Fatalf("%+v", err) + t.Fatalf("Failed to create cipher") } + for _, c := range []cryptoChannel.Cipher{nil, cipher} { + cs := "" + if cipher != nil { + cs = "_withCipher" + } + t.Run(fmt.Sprintf("Test_wasmModel_UpdateSentStatus%s", cs), func(t *testing.T) { + storage.GetLocalStorage().Clear() + testString := "test" + testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1}) + eventModel, err := newWASMModel(testString, c, dummyCallback) + if err != nil { + t.Fatalf("%+v", err) + } - // Store a test message - testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil, - testString, []byte(testString), []byte{8, 6, 7, 5}, 0, netTime.Now(), - time.Second, 0, 0, false, false, channels.Sent) - uuid, err := eventModel.receiveHelper(testMsg, false) - if err != nil { - t.Fatalf("%+v", err) - } + // Store a test message + testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil, + testString, []byte(testString), []byte{8, 6, 7, 5}, 0, netTime.Now(), + time.Second, 0, 0, false, false, channels.Sent) + uuid, err := eventModel.receiveHelper(testMsg, false) + if err != nil { + t.Fatalf("%+v", err) + } - // Ensure one message is stored - results, err := indexedDb.Dump(eventModel.db, messageStoreName) - if err != nil { - t.Fatalf("%+v", err) - } - if len(results) != 1 { - t.Fatalf("Expected 1 message to exist") - } + // Ensure one message is stored + results, err := indexedDb.Dump(eventModel.db, messageStoreName) + if err != nil { + t.Fatalf("%+v", err) + } + if len(results) != 1 { + t.Fatalf("Expected 1 message to exist") + } - // Update the sentStatus - expectedStatus := channels.Failed - eventModel.UpdateFromUUID(uuid, nil, nil, - nil, nil, nil, &expectedStatus) + // Update the sentStatus + expectedStatus := channels.Failed + eventModel.UpdateFromUUID( + uuid, nil, nil, nil, nil, nil, &expectedStatus) - // Check the resulting status - results, err = indexedDb.Dump(eventModel.db, messageStoreName) - if err != nil { - t.Fatalf("%+v", err) - } - if len(results) != 1 { - t.Fatalf("Expected 1 message to exist") - } - resultMsg := &Message{} - err = json.Unmarshal([]byte(results[0]), resultMsg) - if err != nil { - t.Fatalf("%+v", err) - } - if resultMsg.Status != uint8(expectedStatus) { - t.Fatalf("Unexpected Status: %v", resultMsg.Status) - } + // Check the resulting status + results, err = indexedDb.Dump(eventModel.db, messageStoreName) + if err != nil { + t.Fatalf("%+v", err) + } + if len(results) != 1 { + t.Fatalf("Expected 1 message to exist") + } + resultMsg := &Message{} + err = json.Unmarshal([]byte(results[0]), resultMsg) + if err != nil { + t.Fatalf("%+v", err) + } + if resultMsg.Status != uint8(expectedStatus) { + t.Fatalf("Unexpected Status: %v", resultMsg.Status) + } - // Make sure other fields didn't change - if resultMsg.Nickname != testString { - t.Fatalf("Unexpected Nickname: %v", resultMsg.Nickname) + // Make sure other fields didn't change + if resultMsg.Nickname != testString { + t.Fatalf("Unexpected Nickname: %v", resultMsg.Nickname) + } + }) } } // Smoke test wasmModel.JoinChannel/wasmModel.LeaveChannel happy paths. func Test_wasmModel_JoinChannel_LeaveChannel(t *testing.T) { - storage.GetLocalStorage().Clear() - eventModel, err := newWASMModel("test", nil, dummyCallback) + cipher, err := cryptoChannel.NewCipher([]byte("testpass"), []byte("testsalt"), 128, csprng.NewSystemRNG()) if err != nil { - t.Fatalf("%+v", err) + t.Fatalf("Failed to create cipher") } + for _, c := range []cryptoChannel.Cipher{nil, cipher} { + cs := "" + if cipher != nil { + cs = "_withCipher" + } + t.Run(fmt.Sprintf("Test_wasmModel_JoinChannel_LeaveChannel%s", cs), func(t *testing.T) { + storage.GetLocalStorage().Clear() + eventModel, err := newWASMModel("test", c, dummyCallback) + if err != nil { + t.Fatalf("%+v", err) + } - testChannel := &cryptoBroadcast.Channel{ - ReceptionID: id.NewIdFromString("test", id.Generic, t), - Name: "test", - Description: "test", - Salt: nil, - } - testChannel2 := &cryptoBroadcast.Channel{ - ReceptionID: id.NewIdFromString("test2", id.Generic, t), - Name: "test2", - Description: "test2", - Salt: nil, - } - eventModel.JoinChannel(testChannel) - eventModel.JoinChannel(testChannel2) - results, err := indexedDb.Dump(eventModel.db, channelsStoreName) - if err != nil { - t.Fatalf("%+v", err) - } - if len(results) != 2 { - t.Fatalf("Expected 2 channels to exist") - } - eventModel.LeaveChannel(testChannel.ReceptionID) - results, err = indexedDb.Dump(eventModel.db, channelsStoreName) - if err != nil { - t.Fatalf("%+v", err) - } - if len(results) != 1 { - t.Fatalf("Expected 1 channels to exist") + testChannel := &cryptoBroadcast.Channel{ + ReceptionID: id.NewIdFromString("test", id.Generic, t), + Name: "test", + Description: "test", + Salt: nil, + } + testChannel2 := &cryptoBroadcast.Channel{ + ReceptionID: id.NewIdFromString("test2", id.Generic, t), + Name: "test2", + Description: "test2", + Salt: nil, + } + eventModel.JoinChannel(testChannel) + eventModel.JoinChannel(testChannel2) + results, err := indexedDb.Dump(eventModel.db, channelsStoreName) + if err != nil { + t.Fatalf("%+v", err) + } + if len(results) != 2 { + t.Fatalf("Expected 2 channels to exist") + } + eventModel.LeaveChannel(testChannel.ReceptionID) + results, err = indexedDb.Dump(eventModel.db, channelsStoreName) + if err != nil { + t.Fatalf("%+v", err) + } + if len(results) != 1 { + t.Fatalf("Expected 1 channels to exist") + } + }) } } // 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) + cipher, err := cryptoChannel.NewCipher([]byte("testpass"), []byte("testsalt"), 128, csprng.NewSystemRNG()) if err != nil { - t.Fatalf("%+v", err) + t.Fatalf("Failed to create cipher") } + for _, c := range []cryptoChannel.Cipher{nil, cipher} { + cs := "" + if cipher != nil { + cs = "_withCipher" + } + t.Run(fmt.Sprintf("Test_wasmModel_UUIDTest%s", cs), func(t *testing.T) { + storage.GetLocalStorage().Clear() + testString := "testHello" + eventModel, err := newWASMModel(testString, c, dummyCallback) + if err != nil { + t.Fatalf("%+v", err) + } - uuids := make([]uint64, 10) - - for i := 0; i < 10; i++ { - // Store a test message - channelID := id.NewIdFromBytes([]byte(testString), t) - msgID := channel.MessageID{} - copy(msgID[:], testString+fmt.Sprintf("%d", i)) - rnd := rounds.Round{ID: id.Round(42)} - uuid := eventModel.ReceiveMessage(channelID, msgID, "test", - testString+fmt.Sprintf("%d", i), []byte{8, 6, 7, 5}, 0, - netTime.Now(), time.Hour, rnd, 0, channels.Sent, false) - uuids[i] = uuid - } + uuids := make([]uint64, 10) + + for i := 0; i < 10; i++ { + // Store a test message + channelID := id.NewIdFromBytes([]byte(testString), t) + msgID := channel.MessageID{} + copy(msgID[:], testString+fmt.Sprintf("%d", i)) + rnd := rounds.Round{ID: id.Round(42)} + uuid := eventModel.ReceiveMessage(channelID, msgID, "test", + testString+fmt.Sprintf("%d", i), []byte{8, 6, 7, 5}, 0, + netTime.Now(), time.Hour, rnd, 0, channels.Sent, false) + uuids[i] = uuid + } - for i := 0; i < 10; i++ { - for j := i + 1; j < 10; j++ { - if uuids[i] == uuids[j] { - t.Fatalf("uuid failed: %d[%d] == %d[%d]", - uuids[i], i, uuids[j], j) + for i := 0; i < 10; i++ { + for j := i + 1; j < 10; j++ { + if uuids[i] == uuids[j] { + t.Fatalf("uuid failed: %d[%d] == %d[%d]", + uuids[i], i, uuids[j], j) + } + } } - } + }) } } // Tests if the same message ID being sent always returns the same UUID. func Test_wasmModel_DuplicateReceives(t *testing.T) { - storage.GetLocalStorage().Clear() - testString := "testHello" - eventModel, err := newWASMModel(testString, nil, dummyCallback) + cipher, err := cryptoChannel.NewCipher([]byte("testpass"), []byte("testsalt"), 128, csprng.NewSystemRNG()) if err != nil { - t.Fatalf("%+v", err) + t.Fatalf("Failed to create cipher") } + for _, c := range []cryptoChannel.Cipher{nil, cipher} { + cs := "" + if cipher != nil { + cs = "_withCipher" + } + t.Run(fmt.Sprintf("Test_wasmModel_DuplicateReceives%s", cs), func(t *testing.T) { + storage.GetLocalStorage().Clear() + testString := "testHello" + eventModel, err := newWASMModel(testString, c, dummyCallback) + if err != nil { + t.Fatalf("%+v", err) + } - uuids := make([]uint64, 10) - - msgID := channel.MessageID{} - copy(msgID[:], testString) - for i := 0; i < 10; i++ { - // Store a test message - channelID := id.NewIdFromBytes([]byte(testString), t) - rnd := rounds.Round{ID: id.Round(42)} - uuid := eventModel.ReceiveMessage(channelID, msgID, "test", - testString+fmt.Sprintf("%d", i), []byte{8, 6, 7, 5}, 0, - netTime.Now(), time.Hour, rnd, 0, channels.Sent, false) - uuids[i] = uuid - } + uuids := make([]uint64, 10) + + msgID := channel.MessageID{} + copy(msgID[:], testString) + for i := 0; i < 10; i++ { + // Store a test message + channelID := id.NewIdFromBytes([]byte(testString), t) + rnd := rounds.Round{ID: id.Round(42)} + uuid := eventModel.ReceiveMessage(channelID, msgID, "test", + testString+fmt.Sprintf("%d", i), []byte{8, 6, 7, 5}, 0, + netTime.Now(), time.Hour, rnd, 0, channels.Sent, false) + uuids[i] = uuid + } - for i := 0; i < 10; i++ { - for j := i + 1; j < 10; j++ { - if uuids[i] != uuids[j] { - t.Fatalf("uuid failed: %d[%d] != %d[%d]", - uuids[i], i, uuids[j], j) + for i := 0; i < 10; i++ { + for j := i + 1; j < 10; j++ { + if uuids[i] != uuids[j] { + t.Fatalf("uuid failed: %d[%d] != %d[%d]", + uuids[i], i, uuids[j], j) + } + } } - } + }) } } // 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 - eventModel, err := newWASMModel(testString, nil, dummyCallback) + cipher, err := cryptoChannel.NewCipher([]byte("testpass"), []byte("testsalt"), 128, csprng.NewSystemRNG()) if err != nil { - t.Fatalf("%+v", err) + t.Fatalf("Failed to create cipher") } + for _, c := range []cryptoChannel.Cipher{nil, cipher} { + cs := "" + if cipher != nil { + cs = "_withCipher" + } + t.Run(fmt.Sprintf("Test_wasmModel_deleteMsgByChannel%s", cs), func(t *testing.T) { + storage.GetLocalStorage().Clear() + testString := "test_deleteMsgByChannel" + totalMessages := 10 + expectedMessages := 5 + eventModel, err := newWASMModel(testString, c, dummyCallback) + if err != nil { + t.Fatalf("%+v", err) + } - // Create a test channel id - deleteChannel := id.NewIdFromString("deleteMe", id.Generic, t) - keepChannel := id.NewIdFromString("dontDeleteMe", id.Generic, t) + // Create a test channel id + deleteChannel := id.NewIdFromString("deleteMe", id.Generic, t) + keepChannel := id.NewIdFromString("dontDeleteMe", id.Generic, t) - // Store some test messages - for i := 0; i < totalMessages; i++ { - testStr := testString + strconv.Itoa(i) + // Store some test messages + for i := 0; i < totalMessages; i++ { + testStr := testString + strconv.Itoa(i) - // Interleave the channel id to ensure cursor is behaving intelligently - thisChannel := deleteChannel - if i%2 == 0 { - thisChannel = keepChannel - } + // Interleave the channel id to ensure cursor is behaving intelligently + thisChannel := deleteChannel + if i%2 == 0 { + thisChannel = keepChannel + } - testMsgId := channel.MakeMessageID([]byte(testStr), &id.ID{1}) - eventModel.ReceiveMessage(thisChannel, testMsgId, testStr, testStr, - []byte{8, 6, 7, 5}, 0, netTime.Now(), time.Second, - rounds.Round{ID: id.Round(0)}, 0, channels.Sent, false) - } + testMsgId := channel.MakeMessageID([]byte(testStr), &id.ID{1}) + eventModel.ReceiveMessage(thisChannel, testMsgId, testStr, testStr, + []byte{8, 6, 7, 5}, 0, netTime.Now(), time.Second, + rounds.Round{ID: id.Round(0)}, 0, channels.Sent, false) + } - // Check pre-results - result, err := indexedDb.Dump(eventModel.db, messageStoreName) - if err != nil { - t.Fatalf("%+v", err) - } - if len(result) != totalMessages { - t.Errorf("Expected %d messages, got %d", totalMessages, len(result)) - } + // Check pre-results + result, err := indexedDb.Dump(eventModel.db, messageStoreName) + if err != nil { + t.Fatalf("%+v", err) + } + if len(result) != totalMessages { + t.Errorf("Expected %d messages, got %d", totalMessages, len(result)) + } - // Do delete - err = eventModel.deleteMsgByChannel(deleteChannel) - if err != nil { - t.Error(err) - } + // Do delete + err = eventModel.deleteMsgByChannel(deleteChannel) + if err != nil { + t.Error(err) + } - // Check final results - result, err = indexedDb.Dump(eventModel.db, messageStoreName) - if err != nil { - t.Fatalf("%+v", err) - } - if len(result) != expectedMessages { - t.Errorf("Expected %d messages, got %d", expectedMessages, len(result)) + // Check final results + result, err = indexedDb.Dump(eventModel.db, messageStoreName) + if err != nil { + t.Fatalf("%+v", err) + } + if len(result) != expectedMessages { + t.Errorf("Expected %d messages, got %d", expectedMessages, len(result)) + } + }) } } // 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) + cipher, err := cryptoChannel.NewCipher([]byte("testpass"), []byte("testsalt"), 128, csprng.NewSystemRNG()) if err != nil { - t.Fatal(err) + t.Fatalf("Failed to create cipher") } + for i, c := range []cryptoChannel.Cipher{nil, cipher} { + cs := "" + if cipher != nil { + cs = "_withCipher" + } + t.Run(fmt.Sprintf("TestWasmModel_receiveHelper_UniqueIndex%s", cs), func(t *testing.T) { + storage.GetLocalStorage().Clear() + testString := fmt.Sprintf("test_receiveHelper_UniqueIndex_%d", i) + eventModel, err := newWASMModel(testString, c, dummyCallback) + if err != nil { + t.Fatal(err) + } - // Ensure index is unique - txn, err := eventModel.db.Transaction( - idb.TransactionReadOnly, messageStoreName) - if err != nil { - t.Fatal(err) - } - store, err := txn.ObjectStore(messageStoreName) - if err != nil { - t.Fatal(err) - } - idx, err := store.Index(messageStoreMessageIndex) - if err != nil { - t.Fatal(err) - } - if isUnique, err2 := idx.Unique(); !isUnique { - t.Fatalf("Index is not unique!") - } else if err2 != nil { - t.Fatal(err2) - } + // Ensure index is unique + txn, err := eventModel.db.Transaction( + idb.TransactionReadOnly, messageStoreName) + if err != nil { + t.Fatal(err) + } + store, err := txn.ObjectStore(messageStoreName) + if err != nil { + t.Fatal(err) + } + idx, err := store.Index(messageStoreMessageIndex) + if err != nil { + t.Fatal(err) + } + if isUnique, err2 := idx.Unique(); !isUnique { + t.Fatalf("Index is not unique!") + } else if err2 != nil { + t.Fatal(err2) + } - // First message insert should succeed - testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1}) - testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil, - testString, []byte(testString), []byte{8, 6, 7, 5}, 0, netTime.Now(), - time.Second, 0, 0, false, false, channels.Sent) - _, err = eventModel.receiveHelper(testMsg, false) - if err != nil { - t.Fatal(err) - } + // First message insert should succeed + testMsgId := channel.MakeMessageID([]byte(testString), &id.ID{1}) + testMsg := buildMessage([]byte(testString), testMsgId.Bytes(), nil, + testString, []byte(testString), []byte{8, 6, 7, 5}, 0, + netTime.Now(), time.Second, 0, 0, false, false, channels.Sent) + _, err = eventModel.receiveHelper(testMsg, false) + if err != nil { + t.Fatal(err) + } - // The duplicate entry won't fail, but it just silently shouldn't happen - _, err = eventModel.receiveHelper(testMsg, false) - if err != nil { - t.Fatalf("%+v", err) - } - results, err := indexedDb.Dump(eventModel.db, messageStoreName) - if err != nil { - t.Fatalf("%+v", err) - } - if len(results) != 1 { - t.Fatalf("Expected only a single message, got %d", len(results)) - } + // The duplicate entry won't fail, but it just silently shouldn't happen + _, err = eventModel.receiveHelper(testMsg, false) + if err != nil { + t.Fatalf("%+v", err) + } + results, err := indexedDb.Dump(eventModel.db, messageStoreName) + if err != nil { + t.Fatalf("%+v", err) + } + if len(results) != 1 { + t.Fatalf("Expected only a single message, got %d", len(results)) + } - // Now insert a message with a different message ID from the first - testMsgId2 := channel.MakeMessageID([]byte(testString), &id.ID{2}) - testMsg = buildMessage([]byte(testString), testMsgId2.Bytes(), nil, - testString, []byte(testString), []byte{8, 6, 7, 5}, 0, netTime.Now(), - time.Second, 0, 0, false, false, channels.Sent) - primaryKey, err := eventModel.receiveHelper(testMsg, false) - if err != nil { - t.Fatal(err) - } + // Now insert a message with a different message ID from the first + testMsgId2 := channel.MakeMessageID([]byte(testString), &id.ID{2}) + testMsg = buildMessage([]byte(testString), testMsgId2.Bytes(), nil, + testString, []byte(testString), []byte{8, 6, 7, 5}, 0, + netTime.Now(), time.Second, 0, 0, false, false, channels.Sent) + primaryKey, err := eventModel.receiveHelper(testMsg, false) + if err != nil { + t.Fatal(err) + } - // Except this time, we update the second entry to have the same - // message ID as the first - testMsg.ID = primaryKey - testMsg.MessageID = testMsgId.Bytes() - _, err = eventModel.receiveHelper(testMsg, true) - if err != nil { - t.Fatal(err) - } + // Except this time, we update the second entry to have the same + // message ID as the first + testMsg.ID = primaryKey + testMsg.MessageID = testMsgId.Bytes() + _, err = eventModel.receiveHelper(testMsg, true) + if err != nil { + t.Fatal(err) + } - // The update to duplicate message ID won't fail, - // but it just silently shouldn't happen - results, err = indexedDb.Dump(eventModel.db, messageStoreName) - if err != nil { - t.Fatalf("%+v", err) + // The update to duplicate message ID won't fail, + // but it just silently shouldn't happen + results, err = indexedDb.Dump(eventModel.db, messageStoreName) + if err != nil { + t.Fatalf("%+v", err) + } + // TODO: Convert JSON to Message, ensure Message ID fields differ + + }) } - // TODO: Convert JSON to Message, ensure Message ID fields differ } diff --git a/storage/version.go b/storage/version.go index 645a8bbdd974a89dff073f3c8fc1db35ce7f7875..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.10" +const SEMVER = "0.1.13" // Storage keys. const ( diff --git a/utils/convert.go b/utils/convert.go index 0c2d443ecb16b9992b3f87805b4833935bdaeb2a..a5c79e309529089ebb918fc77f701f7a633c8d05 100644 --- a/utils/convert.go +++ b/utils/convert.go @@ -32,6 +32,10 @@ func CopyBytesToJS(src []byte) js.Value { // JsToJson converts the Javascript value to JSON. func JsToJson(value js.Value) string { + if value.IsUndefined() { + return "null" + } + return JSON.Call("stringify", value).String() } diff --git a/utils/convert_test.go b/utils/convert_test.go index 8cbbf2555c307c782bb3f52168c12ec307633fd4..74bd9ca6c68e5e95639f413f2da0ab58b5faed31 100644 --- a/utils/convert_test.go +++ b/utils/convert_test.go @@ -113,6 +113,22 @@ func TestJsToJson(t *testing.T) { } } +// Tests that JsToJson return a null object when the Javascript object is +// undefined. +func TestJsToJson_Undefined(t *testing.T) { + expected, err := json.Marshal(nil) + if err != nil { + t.Errorf("Failed to JSON marshal test object: %+v", err) + } + + jsJson := JsToJson(js.Undefined()) + + if string(expected) != jsJson { + t.Errorf("Recieved incorrect JSON from Javascript object."+ + "\nexpected: %s\nreceived: %s", expected, jsJson) + } +} + // Tests that JsonToJS can convert a JSON object with multiple types to a // Javascript object and that all values match. func TestJsonToJS(t *testing.T) {