summaryrefslogtreecommitdiff
path: root/test/ui/UiLogicTest.mc
blob: 01b7dae23ffc13cc4d65950c91a5c3cc8c666674 (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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// Author:  cirakg & claude ai
// Summary: Tests for the UI's logic without a screen: the code cache, code grouping, text helpers and option defaults.

import Toybox.Lang;
import Toybox.Test;

(:test)
function codeCacheSplitsCodesForReading(logger as Test.Logger) as Boolean {
    var cases = [["123456", "123|456"], ["1234567", "123|4567"], ["12345678", "1234|5678"], ["R7VXK", "R7VXK"]];
    for (var i = 0; i < cases.size(); i++) {
        var c = cases[i] as Array<String>;
        var parts = CodeCache.split(c[0]);
        var joined = parts.size() == 2 ? parts[0] + "|" + parts[1] : parts[0];
        if (!TestSupport.sameString(logger, "split " + c[0], joined, c[1])) {
            return false;
        }
    }
    return true;
}

// The cache gives the same codes as the OTP module, keeps them until the
// period ends, and learns the period and cost from the encrypted part.
(:test)
function codeCacheComputesAndExpiresCodes(logger as Test.Logger) as Boolean {
    var vault = new Vault(new MemoryBackend(), new FakeTime());
    var e = StorageFixtures.totp("Cache");
    var secret = Bytes.copy(e.secret as ByteArray);
    var id = vault.add(e);
    var cache = new CodeCache(vault);
    var now = 1800000026l; // 34 s left in the fixture's 60 s period
    var expected = Otp.totp(Hashes.SHA256, secret, now, 60, 8);
    var next = Otp.totp(Hashes.SHA256, secret, now + 60, 60, 8);
    var unknownCost = cache.costOf(e);
    var code = cache.code(id, now, 0);
    var stillCached = cache.cached(id, now + 33) != null;
    var expired = cache.cached(id, now + 34) == null;
    return TestSupport.sameString(logger, "current", code, expected)
        && TestSupport.sameString(logger, "next", cache.code(id, now, 1), next)
        && stillCached && expired && cache.periodOf(id) == 60
        && unknownCost == CodeCache.COST_SHA512 && cache.costOf(e) == CodeCache.COST_NATIVE;
}

(:test)
function codeCacheHotpAndInvalidate(logger as Test.Logger) as Boolean {
    var vault = new Vault(new MemoryBackend(), new FakeTime());
    var id = vault.add(StorageFixtures.hotp("Counter", 5l));
    var cache = new CodeCache(vault);
    var first = cache.code(id, 0l, 0);
    vault.setCounter(id, 6l);
    var stale = cache.code(id, 0l, 0);
    cache.invalidate(id);
    var fresh = cache.code(id, 0l, 0);
    // RFC 4226 Appendix D: counters 5 and 6.
    return first.equals("254676") && stale.equals("254676") && fresh.equals("287922");
}

(:test)
function codeCacheRefusesLockedEntriesWhileLocked(logger as Test.Logger) as Boolean {
    var vault = new Vault(new MemoryBackend(), new FakeTime());
    StorageFixtures.setPin(vault, StorageFixtures.PIN);
    var e = StorageFixtures.totp("Locked");
    e.locked = true;
    var id = vault.add(e);
    var cache = new CodeCache(vault);
    var unlockedCode = cache.code(id, 1800000026l, 0);
    vault.lock();
    cache.clear();
    var code = 0;
    try {
        cache.code(id, 1800000026l, 0);
    } catch (err instanceof VaultError) {
        code = err.code;
    }
    return unlockedCode.length() == 8 && code == VaultError.LOCKED && cache.cached(id, 1800000026l) == null;
}

(:test)
function uiTextHelpers(logger as Test.Logger) as Boolean {
    var parts = Ui.split("a b  c", " ");
    return Ui.isDigits("123 456") && !Ui.isDigits("12a") && !Ui.isDigits("") && !Ui.isDigits("------")
        && parts.size() == 4 && parts[0].equals("a") && parts[2].equals("") && parts[3].equals("c")
        && Ui.minutesSeconds(90).equals("1:30") && Ui.minutesSeconds(3600).equals("60:00") && Ui.minutesSeconds(5).equals("0:05");
}

// resources/settings/properties.xml defaults give README 10.2's defaults.
(:test)
function optionsDefaults(logger as Test.Logger) as Boolean {
    var o = new Options();
    o.load();
    return o.autoLockMs == 60000 && !o.eraseAfterFailures && o.timeOffset == 0 && o.codeSize == Options.CODE_LARGE
        && o.theme == Options.THEME_DARK && o.showGroups && !o.encryptNames && !o.glanceCode && o.fullBrightness;
}