aboutsummaryrefslogtreecommitdiff
path: root/test/storage/MigrationTest.mc
blob: 046ee04476596b015dc45b0725a0508416afcd32 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// Author:  cirakg & claude ai
// Summary: Tests for schema migration: fresh store, the v0 fixture to v1, idempotency, newer and damaged metadata.

import Toybox.Application;
import Toybox.Lang;
import Toybox.Test;

(:test)
function migrationCreatesFreshMetadata(logger as Test.Logger) as Boolean {
    var store = new MemoryBackend();
    var vault = new Vault(store, new FakeTime());
    var meta = store.getValue(Migration.META_KEY) as Dictionary<Application.Storage.KeyType, Application.Storage.ValueType>;
    return vault.ids().size() == 0 && (meta["schema"] as Number) == Migration.CURRENT;
}

// The v0 fixture: entries written in the current record format, listed by
// the old "order" key. After migration they open as before, and running the
// migration again changes nothing.
(:test)
function migrationV0ToV1(logger as Test.Logger) as Boolean {
    var store = new MemoryBackend();
    var vault = new Vault(store, new FakeTime());
    var a = vault.add(StorageFixtures.totp("A"));
    var b = vault.add(StorageFixtures.code("B"));
    store.setValue(Migration.META_KEY, {"order" => [b, a]});
    var migrated = new Vault(store, new FakeTime());
    var ids = migrated.ids();
    var meta = store.getValue(Migration.META_KEY) as Dictionary<Application.Storage.KeyType, Application.Storage.ValueType>;
    var opened = migrated.open(a);
    opened.wipeSecrets();
    var again = new Vault(store, new FakeTime());
    return ids.size() == 2 && ids[0].equals(b) && ids[1].equals(a) && (meta["schema"] as Number) == 1
        && !meta.hasKey("order") && again.ids().size() == 2 && again.pendingIds().size() == 0;
}

(:test)
function migrationRefusesNewerAndDamagedMetadata(logger as Test.Logger) as Boolean {
    var cases = [
        {"schema" => Migration.CURRENT + 1, "ids" => [], "pending" => []},
        "not a dictionary",
        {"schema" => "1", "ids" => [], "pending" => []},
        {"schema" => 1, "ids" => ["K7F2", "K7F2"], "pending" => []},
        {"schema" => 1, "ids" => ["bad"], "pending" => []},
        {"schema" => 1, "ids" => [], "pending" => 5}
    ];
    var expected = [VaultError.NEWER_SCHEMA, VaultError.DAMAGED, VaultError.DAMAGED, VaultError.DAMAGED,
        VaultError.DAMAGED, VaultError.DAMAGED];
    for (var i = 0; i < cases.size(); i++) {
        var store = new MemoryBackend();
        store.setValue(Migration.META_KEY, cases[i] as Application.Storage.ValueType);
        var code = 0;
        try {
            new Vault(store, new FakeTime());
        } catch (err instanceof VaultError) {
            code = err.code;
        }
        if (code != expected[i]) {
            logger.error("case " + i + ": got " + code);
            return false;
        }
        // Newer or damaged data is never modified.
        var after = store.getValue(Migration.META_KEY);
        if (cases[i] instanceof String ? !(after as String).equals(cases[i] as String) : MemoryBackend.sizeOf(after) != MemoryBackend.sizeOf(cases[i] as Application.Storage.ValueType)) {
            logger.error("case " + i + ": metadata was modified");
            return false;
        }
    }
    return true;
}