summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorcirakg <cirakg@cirakg.xyz>2026-10-20 01:51:05 +0200
committercirakg <cirakg@cirakg.xyz>2026-10-20 01:51:05 +0200
commit42f9331e693f5058fcfcd7aef5a92cabc04c20ee (patch)
treef3dab2657eb592ee468e6cebc9b47d9da7e08683 /test
parentd537f4e154a40075405b0ab2fed14fbe2eeaac81 (diff)
test(import): cover parsers, importer and a seeded fuzz corpus; import 100 entries in the UI flow
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'test')
-rw-r--r--test/data/FuzzCorpus.mc115
-rw-r--r--test/golden/fenix7/about.pngbin3828 -> 4037 bytes
-rw-r--r--test/golden/fenix7/import_results.pngbin0 -> 4462 bytes
-rw-r--r--test/golden/fr255s/about.pngbin2532 -> 2385 bytes
-rw-r--r--test/golden/fr255s/import_results.pngbin0 -> 2864 bytes
-rw-r--r--test/golden/fr70/about.pngbin15633 -> 15606 bytes
-rw-r--r--test/golden/fr70/import_results.pngbin0 -> 13389 bytes
-rw-r--r--test/golden/fr965/about.pngbin17081 -> 15984 bytes
-rw-r--r--test/golden/fr965/import_results.pngbin0 -> 15853 bytes
-rw-r--r--test/golden/instinct3amoled45mm/about.pngbin13211 -> 13651 bytes
-rw-r--r--test/golden/instinct3amoled45mm/import_results.pngbin0 -> 14894 bytes
-rw-r--r--test/golden/venusq2/import_results.pngbin0 -> 4825 bytes
-rw-r--r--test/golden/venux1/import_results.pngbin0 -> 21995 bytes
-rw-r--r--test/import/AegisTest.mc128
-rw-r--r--test/import/FuzzTest.mc107
-rw-r--r--test/import/ImportSupport.mc76
-rw-r--r--test/import/ImporterTest.mc208
-rw-r--r--test/import/JsonTest.mc128
-rw-r--r--test/import/OtpAuthTest.mc99
19 files changed, 861 insertions, 0 deletions
diff --git a/test/data/FuzzCorpus.mc b/test/data/FuzzCorpus.mc
new file mode 100644
index 0000000..57bdf8b
--- /dev/null
+++ b/test/data/FuzzCorpus.mc
@@ -0,0 +1,115 @@
+// Author: cirakg & claude ai
+// Summary: Seeded fuzz inputs for the import parsers (generated by tools/gen_fuzz_corpus.py; do not edit).
+//
+// Seed 4242. Hex because many inputs are not valid UTF-8. Fake data only.
+
+import Toybox.Lang;
+
+(:test)
+module FuzzCorpus {
+ const JSON = [
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f74705c75222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223aa07b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20375d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e7977616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2256acb676657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c20226469676974", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e6731225d7d2c207b2274797070222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147456e756c6c5a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c20223d656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c90f620226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c20226f756e746572223a20377d7d5d726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354510f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a2241414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141412251222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f222a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c20226122534841323536222c2022646967697422534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c202261676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223d3a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d6522", // lint: allow-literal
+ "7b22766572224141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141412273696f6e223a20312c2022686561646572223a207b", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a444754514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c20225c7564383364686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c202269737375655b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b72223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c20225b5b5b5b5b5b5b5b5b5b5b5b69636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c202269756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c20", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e4257615647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147415a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d2241412241414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414122414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141225d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c202264622241414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141412241414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141224141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141415141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414122223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c20226e756c6c706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a205b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b22616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e42564759333a54514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470225c75643833642c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563626574223a202247455a593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2ce42a20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f22223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c2022753a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657220312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253485b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b4131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275755b5b5b5b5b5b5b5b5b5b5b5b5b5b6964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d7d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205a7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a202247695b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b74487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269de9e047a636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e3d616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a3a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222ce0e51b20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a5122", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022692d897373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223d3a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c203c5b8a4122656e7472696573223a225b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f7570735b5b5b5b5b5b5b5b5b5b223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64222a3a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022645b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b6967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c5d202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f74702224202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a20224769744875627465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f6563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636b6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b227479706522322022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f6e756c6c72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a4447", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c476f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f22223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a444722414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141224e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c205b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b7b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c2022673d726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472224141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414122696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b2267202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756570223a202256504e222c207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022646967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d", // lint: allow-literal
+ "7b2276657273696f6e223a20312c2022686561646572223a207b22736c6f7473223a206e756c6c2c2022706172616d73223a206e756c6c7d2c20226462223a207b2276657273696f6e223a20332c2022656e7472696573223a205b7b2274797065223a2022746f7470222c202275756964223a20227531222c20226e616d65223a2022616c696365222c2022697373756572223a2022476974487562222c20226e6f7465223a2022222c20226661766f72697465223a20747275652c202269636f6e223a206e756c6c2c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a202253484131222c2022646967697473223a20362c2022706572696f64223a2033307d2c202267726f757073223a205b226731225d7d2c207b2274797065223a2022686f7470222c20226e616d65223a2022625c753030653962222c2022697373756572223a202256504e222c2022696e666f223a207b22736563726574223a202247455a44474e425647593354514f4a5147455a44474e425647593354514f4a51222c2022616c676f223a2022534841323536222c2022642241414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141226967697473223a20382c2022706572696f64223a2033302c2022636f756e746572223a20377d7d5d2c202267726f757073223a205b7b2275756964223a20226731222c20226e616d65223a2022576f726b227d5d7d7d" // lint: allow-literal
+ ];
+
+ const URIS = [
+ "6f7470617574683a2f2f746f74702f4769744875623a616c6963652534306578616d706c652e636f6d3f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a51266973737565723d476974487562266469676974733d36", // lint: allow-literal
+ "6f7470617574683a2f2f686f74702f56504e3a626f623f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a5126636f756e7465723d353b616c676f726974686d3d534841323536", // lint: allow-literal
+ "6f7470617574683a2f2f737465616d2f537465616d3a6361726f6c3f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a51", // lint: allow-literal
+ "6f7470617574683a2f2f686f74702f56504e3a626f623f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a5126636f756e7465723d353b616c676f726974686d3d535841323536", // lint: allow-literal
+ "6f7470617574683a2f2f746f74702f4769744875623a616c6963652534306578616d706c652e636f6d3f7365637265743d47455a44574e425647593354514f4a5147455a44474e425647593354514f4a51266973737565723d4769746973737565723d476974487562266469676974733d36", // lint: allow-literal
+ "6f7470617574683a2f2f686f74702f56504e3a626f623f736526637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a5126636f756e7465723d353b616c676f726974686d3d534841224141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141", // lint: allow-literal
+ "6f747061", // lint: allow-literal
+ "6f7470613a2f2f686f74702f56504e3a626f623f7365637265743d47455a44474e425647593354514f5a44474e4256475933544a5126636f756e7465723d353b616c676f726974686d3d534841323536", // lint: allow-literal
+ "6f7470617574683a2f2f686f74702f56504e3a626f623f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a51266365723d353b616c676f726974686d3d534841323536", // lint: allow-literal
+ "6f7470b8617574683a2f2f747f74702f476974", // lint: allow-literal
+ "6f7470617574683a2f2f746f74702f4769744875623a616c6963652534302c6578616d706c652e636f6d3f7365637265743d47455aac0b44474e425647593354514f4a5147455a44474e425647593354514f4a51266973737565723d476974487562", // lint: allow-literal
+ "6f7470617525fb737465616d2f537465616d3a6361726f6c3f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a51", // lint: allow-literal
+ "6f7470617574683a2f2f746f74702f4769744875623a616c6963652534306578616d224141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414122706c652e636f6d3f7365637265743d47455a44474e425647593354514f4a5147455a44474e42564759224141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141223354514f4a51266973737565723d476974487562266469676974733d36", // lint: allow-literal
+ "6f7470617574683a2f2f746f74702f4769744875623a616c6963652534306578616d706c252e636f6d3f7365637265743d47455a44474e4256475933545111e8b9004f4a5147455a44474e425647593354514f4a51266973737565723d476974487562266469676974733d36", // lint: allow-literal
+ "", // lint: allow-literal
+ "6f7470617574683a2f2f746f7470703bd42f4769744875623a616c6963652534306578616d706c652e636f6d3f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a51266973737565723d476974487562266469676974733d36", // lint: allow-literal
+ "6f7470617574683a2f2f686f74623f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a5126636f756e7465723d353b616c676f726974686d3d534841323536", // lint: allow-literal
+ "6f747244af70617574683a2f2f737465616d2f537465616d3a6361726f6c3f7365637265743d47455a44474e425647593354514f4a5147455a6144474e425647593354514f4a51", // lint: allow-literal
+ "6f7470617574683a2f2f737465616d2f537465616d3a6361726f6c3f7365637265743d47455a44474e42564f4a5147455a44474e425647593354514f4a224141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141412251", // lint: allow-literal
+ "6f7470617574683ae0e32f2f737465616d2f6561", // lint: allow-literal
+ "6f747061757422683a2f2f73683a2f2f737465616d2f537465616d3a6361726f6c3f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a51", // lint: allow-literal
+ "6f7470617574683a2f2f686f74702f56504e3a626f623f736563723a626f623f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f22414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141afed4141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141224a5126636f756e7465723d353b616c676f726974686d3d534841323536", // lint: allow-literal
+ "6f742d70617574683a2f2f686f74702f56504e3a626f623f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a5126636f756e7465723d353b616c676f7269746e7465723d353b616c676f726974686d3d534841323536", // lint: allow-literal
+ "6f7470617574683a2f2f746f74702f4769744875623a616c6963652534306578616d706c652e636f6d3f7365637265743d47455a44474e425647593354514f4a5147455a44475b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b4e425647593354514f4a51266973737565723d476974487562266469676974733d36", // lint: allow-literal
+ "6f7470617574", // lint: allow-literal
+ "6f7470617574683a2f2f746f74702f4769744875623a616c6963652534306578616d706c652e636f6d3f7365637265743d47455a44474e4256475933455a44474e425647593354514f4a51266973737565723d476974487562266469676974733d36", // lint: allow-literal
+ "6f7470617574683a2f2f746f74702f4769744875623a616c6963652534306578616d706c652e636f6d3f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a51266973a3b5737565723d476974487562266469676974733d36", // lint: allow-literal
+ "6f7470617574683a2f2f686f74702f56504e3a626f623f7365637265743d4745", // lint: allow-literal
+ "6f7470617574683a2f2f686f74702f56504e3a626f623f7365637265743d47455a44474e425647623f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a5126636f756e7465723d353b616c676f72697468593354514f4a5126636f756e7465723d353b616c676f726974686d3d534841323536", // lint: allow-literal
+ "6f747061757468cda2736f3a2f2f737465616d2f537465616d3a6361726f6c3f73656372228bb7eb65743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a51", // lint: allow-literal
+ "6f7470617574683a2f2f686f74702f56504e3a626f623f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a5126636f756e475147455a44474e425647593356514f4a5126636f756e47593354514f4a5126636f756e7465723d353b616c676f726974686d3d534841323536", // lint: allow-literal
+ "6f7470617574683a2f2f746f74702f4769744875623a616c69636525345b5b5b5b5b5b5b5b5b5b5b5b5b306578616d706c652e636f6d3f7365637265743d47455a", // lint: allow-literal
+ "6f5b5b5b5b5b4b5b5b5b5b5b5b5b5b747061", // lint: allow-literal
+ "6f2f2f686f74702f56504e3a626f623f7365637265743d47455a44474e425647593354514f4a514745", // lint: allow-literal
+ "6f7470617574683a2f2f686f74702f56504e3a626f623f7365637265743d47455a44474efb7d00425647593354514f4a5147455a44474e425647593354514f4a5126636f756e7465723d353b616c676f726974686d3d5348413235364a5126636f756e7465723d353b616c676f726974686d3d534841323536", // lint: allow-literal
+ "6f74706175e87b49f374683a2f2f746f74702f4769744875623a616c6963652534306578616d706c652e636f6d3f7365637265743d47455a44474e425647593354514f4a5147455a5c44474e425647593354514f4a51266973737565", // lint: allow-literal
+ "6f7470617574683a", // lint: allow-literal
+ "6f7470617574683a2f2f746f74702f4769744875623a616c6963652534306578616d706c6571032e636f6d652e636f6d", // lint: allow-literal
+ "6f7470617574683aaf2f746f74702f4769744875623a616c6963652534306578616d706c652e636f6d3f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a51266973737565723d476974487562266469676974733d36", // lint: allow-literal
+ "6f7470617574683a2f2f742241414141414141414141414141414141414141414141414141414141", // lint: allow-literal
+ "6f7470617574683a2f2f746f74702f4769744875623a616c6963652534306578616d706c652e636e5b5b5b5b5b5b5b5b5b5b5b6d3f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a51266973737565723d476974487562266469676974733d36", // lint: allow-literal
+ "6f7470617574683a2f2f746f74702f4769744875623a616c6963652534306578616d706c652e636f6d3f7365637265743d47455a44474e425647593354514f4a51475d455a44474e425647593354514f4a51266973737565723d476974487562266469676974733d36", // lint: allow-literal
+ "6f7470617574683a2f2f5b5b5b5b5b5b8956945b5b5b5b5b5b5b5b5b5b5b5b5b737465616d2f537465616d3a6361726f6c3f7365637265743d47455a44474e425647593354514f4a514745", // lint: allow-literal
+ "6f7470617574683a2f2f737465616d2f537465616d3a6361726f6c3f7365637265743d475b5b5b5b5b5b5b5b5b966dcec25b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b5b455a44474e425647593354514f4a5147455a44474e425647593354514f4a51", // lint: allow-literal
+ "6f7470617574683a2f2f746f74702f4769744875623a616c6963652534306578616d706c652e636f6d3f7365637265743d47455a44474e4256471b3354514f4a5147455a44474e425647593354514f4a51266973737565723d476974487562266469676974733d366974487562266469676974733d36", // lint: allow-literal
+ "6f7470617574683a2f2f737465616d2f537465616d3a6361726f6c3a2f2f737465616d2f537465616d3a6361726f6c3f7365637265743d47455a44474e425647593354726f6c3f7365637265743d47455a44474e425647593354514f4a5147455a44474e42564759223354514f4a51", // lint: allow-literal
+ "22414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141226f", // lint: allow-literal
+ "6f7470617574683a2f2f746f74702f4769744875623a4769744875623a616c69636525343065785c616d706c652e636f6d3f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a51266973737565723d476974487562266469676974733d36", // lint: allow-literal
+ "6f7470617574683a2f2f737465616d2f537465616d3a6361726f6c3f7365637265743d47455a44474e425647593354514f4a5147455a44474e4256477b593354514f4a51", // lint: allow-literal
+ "6f7470617574683a2f2f737465616d2f5374656154334c7d6d3a6361726f6c3f7365637265743d47455a44474e425647593354514f4a5147455a44474e425647593354514f4a51" // lint: allow-literal
+ ];
+}
diff --git a/test/golden/fenix7/about.png b/test/golden/fenix7/about.png
index d6a87a2..3f74946 100644
--- a/test/golden/fenix7/about.png
+++ b/test/golden/fenix7/about.png
Binary files differ
diff --git a/test/golden/fenix7/import_results.png b/test/golden/fenix7/import_results.png
new file mode 100644
index 0000000..34c1cf7
--- /dev/null
+++ b/test/golden/fenix7/import_results.png
Binary files differ
diff --git a/test/golden/fr255s/about.png b/test/golden/fr255s/about.png
index f8f23d2..b2f2548 100644
--- a/test/golden/fr255s/about.png
+++ b/test/golden/fr255s/about.png
Binary files differ
diff --git a/test/golden/fr255s/import_results.png b/test/golden/fr255s/import_results.png
new file mode 100644
index 0000000..d2cdc19
--- /dev/null
+++ b/test/golden/fr255s/import_results.png
Binary files differ
diff --git a/test/golden/fr70/about.png b/test/golden/fr70/about.png
index c46fa11..d7d18a0 100644
--- a/test/golden/fr70/about.png
+++ b/test/golden/fr70/about.png
Binary files differ
diff --git a/test/golden/fr70/import_results.png b/test/golden/fr70/import_results.png
new file mode 100644
index 0000000..2da3dcc
--- /dev/null
+++ b/test/golden/fr70/import_results.png
Binary files differ
diff --git a/test/golden/fr965/about.png b/test/golden/fr965/about.png
index 61b9745..65e3902 100644
--- a/test/golden/fr965/about.png
+++ b/test/golden/fr965/about.png
Binary files differ
diff --git a/test/golden/fr965/import_results.png b/test/golden/fr965/import_results.png
new file mode 100644
index 0000000..c827d20
--- /dev/null
+++ b/test/golden/fr965/import_results.png
Binary files differ
diff --git a/test/golden/instinct3amoled45mm/about.png b/test/golden/instinct3amoled45mm/about.png
index 1400601..75ad5de 100644
--- a/test/golden/instinct3amoled45mm/about.png
+++ b/test/golden/instinct3amoled45mm/about.png
Binary files differ
diff --git a/test/golden/instinct3amoled45mm/import_results.png b/test/golden/instinct3amoled45mm/import_results.png
new file mode 100644
index 0000000..0dbf559
--- /dev/null
+++ b/test/golden/instinct3amoled45mm/import_results.png
Binary files differ
diff --git a/test/golden/venusq2/import_results.png b/test/golden/venusq2/import_results.png
new file mode 100644
index 0000000..647ca21
--- /dev/null
+++ b/test/golden/venusq2/import_results.png
Binary files differ
diff --git a/test/golden/venux1/import_results.png b/test/golden/venux1/import_results.png
new file mode 100644
index 0000000..1d1251d
--- /dev/null
+++ b/test/golden/venux1/import_results.png
Binary files differ
diff --git a/test/import/AegisTest.mc b/test/import/AegisTest.mc
new file mode 100644
index 0000000..eaad039
--- /dev/null
+++ b/test/import/AegisTest.mc
@@ -0,0 +1,128 @@
+// Author: cirakg & claude ai
+// Summary: Tests for the Aegis reader: database versions 1-3, types, parameters, groups, icons, encryption and bad input.
+
+import Toybox.Lang;
+import Toybox.Test;
+
+// Database version 3: types, algorithms, digits, periods, groups listed
+// after the entries, favourites, icons and Unicode.
+(:test)
+function aegisReadsDatabaseVersion3(logger as Test.Logger) as Boolean {
+ var icon = "";
+ for (var i = 0; i < 200; i++) {
+ icon += "iVBORw0KGgoAAAANSUhEUgAAA";
+ }
+ var entries = [
+ AegisFixtures.entry("totp", "GitHub", "alice", AegisFixtures.SECRET_1, "SHA1", 6, 30,
+ "\"icon\": \"" + icon + "\", \"icon_mime\": \"image/png\", \"groups\": [\"g-work\", \"g-other\"], "),
+ AegisFixtures.entry("totp", "Bänk €", "bob", AegisFixtures.SECRET_2, "SHA256", 8, 60, "\"favorite\": true, "),
+ "{\"type\": \"hotp\", \"name\": \"carol\", \"issuer\": \"VPN\", \"info\": {\"secret\": \"" + AegisFixtures.SECRET_3
+ + "\", \"algo\": \"SHA512\", \"digits\": 7, \"period\": 30, \"counter\": 12}}",
+ AegisFixtures.entry("steam", "Steam", "dave", AegisFixtures.SECRET_1, "SHA1", 5, 30, ""),
+ AegisFixtures.entry("motp", "Old", "erin", AegisFixtures.SECRET_1, "MD5", 6, 10, ""),
+ AegisFixtures.entry("yandex", "Ya", "frank", AegisFixtures.SECRET_1, "SHA256", 8, 30, ""),
+ AegisFixtures.entry("totp", "Odd", "gina", AegisFixtures.SECRET_1, "SHA1", 6, 20, "\"groups\": [\"g-missing\"], "),
+ AegisFixtures.entry("totp", "Slow", "hal", AegisFixtures.SECRET_1, "SHA1", 6, 50, "")
+ ];
+ var groups = "[{\"uuid\": \"g-work\", \"name\": \"Work\"}, {\"uuid\": \"g-other\", \"name\": \"Other\"}]";
+ var reader = AegisFixtures.read(AegisFixtures.export(3, entries, groups));
+ if (reader.error != null || reader.items.size() != 8) {
+ logger.error("error " + reader.error + ", items " + reader.items.size());
+ return false;
+ }
+ var it = reader.items;
+ var github = it[0].entry;
+ var bank = it[1].entry;
+ var vpn = it[2].entry;
+ var steam = it[3].entry;
+ var odd = it[6].entry;
+ var slow = it[7].entry;
+ if (github == null || bank == null || vpn == null || steam == null || odd == null || slow == null) {
+ for (var i = 0; i < it.size(); i++) {
+ logger.error("item " + i + ": " + it[i].problem + " / " + it[i].skipped);
+ }
+ return false;
+ }
+ var checks = [
+ github.name.equals("GitHub") && github.account.equals("alice"),
+ github.group.equals("Work"),
+ bank.name.equals("Bänk €") && bank.algorithm == Hashes.SHA256 && bank.digits == 8 && bank.period == 60,
+ bank.favorite,
+ vpn.type == Entry.OTP_HOTP && vpn.counter == 12l && vpn.digits == 7 && vpn.algorithm == Hashes.SHA512,
+ steam.type == Entry.OTP_STEAM,
+ it[4].skipped != null && it[5].skipped != null,
+ odd.group.equals("") && odd.period == 20 && slow.period == 50,
+ Bytes.equal(github.secret as ByteArray, Bytes.fromUtf8("12345678901234567890"))
+ ];
+ for (var i = 0; i < checks.size(); i++) {
+ if (!(checks[i] as Boolean)) {
+ logger.error("check " + i + " failed");
+ return false;
+ }
+ }
+ return true;
+}
+
+// Database versions 1 and 2 name the group directly.
+(:test)
+function aegisReadsOlderDatabaseVersions(logger as Test.Logger) as Boolean {
+ for (var v = 1; v <= 2; v++) {
+ var reader = AegisFixtures.read(AegisFixtures.export(v,
+ [AegisFixtures.entry("totp", "Mail", "ann", AegisFixtures.SECRET_1, "SHA1", 6, 30, "\"group\": \"Private\", ")], ""));
+ var e = reader.items.size() == 1 ? reader.items[0].entry : null;
+ if (e == null || !e.group.equals("Private")) {
+ logger.error("database version " + v + " failed: " + reader.error);
+ return false;
+ }
+ }
+ return true;
+}
+
+(:test)
+function aegisRefusesEncryptedExports(logger as Test.Logger) as Boolean {
+ var slots = "{\"version\": 1, \"header\": {\"slots\": [{\"type\": 1}], \"params\": {}}, \"db\": \"c2VjcmV0\"}";
+ var dbString = "{\"version\": 1, \"header\": {\"slots\": null}, \"db\": \"c2VjcmV0\"}";
+ var a = AegisFixtures.read(slots);
+ var b = AegisFixtures.read(dbString);
+ return a.error != null && (a.error as String).find("encrypted") != null
+ && b.error != null && (b.error as String).find("encrypted") != null;
+}
+
+(:test)
+function aegisReportsBadEntriesAndDocuments(logger as Test.Logger) as Boolean {
+ var entries = [
+ AegisFixtures.entry("totp", "Nine", "x", AegisFixtures.SECRET_1, "SHA1", 9, 30, ""),
+ "{\"type\": \"totp\", \"name\": \"dup\", \"info\": {\"secret\": \"" + AegisFixtures.SECRET_1 + "\", \"secret\": \""
+ + AegisFixtures.SECRET_2 + "\", \"algo\": \"SHA1\", \"digits\": 6, \"period\": 30}}",
+ AegisFixtures.entry("totp", "Bad", "y", "NOT-BASE32!", "SHA1", 6, 30, ""),
+ "{\"type\": \"totp\", \"name\": \"z\", \"info\": {\"secret\": \"" + AegisFixtures.SECRET_1 + "\", \"digits\": 6.5}}"
+ ];
+ var reader = AegisFixtures.read(AegisFixtures.export(3, entries, ""));
+ var it = reader.items;
+ if (reader.error != null || it.size() != 4) {
+ logger.error("error " + reader.error);
+ return false;
+ }
+ var problems = [it[0].problem, it[1].problem, it[2].problem, it[3].problem];
+ var needles = ["9 digits", "twice", "Base32", "whole number"];
+ for (var i = 0; i < 4; i++) {
+ var p = problems[i];
+ if (p == null || (p as String).find(needles[i]) == null) {
+ logger.error("entry " + i + ": " + p);
+ return false;
+ }
+ }
+ var notJson = AegisFixtures.read("{\"version\": 1, \"db\": [1,]}");
+ var notAegis = AegisFixtures.read("{\"hello\": \"world\"}");
+ var newer = AegisFixtures.read("{\"version\": 2, \"db\": {\"version\": 3, \"entries\": []}}");
+ var deep = "{\"version\": 1, \"x\": " + "[[[[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]]]]" + "}";
+ return notJson.error != null && notAegis.error != null && newer.error != null
+ && (newer.error as String).find("version 2") != null && AegisFixtures.read(deep).error != null;
+}
+
+(:test)
+function aegisSaysWhenTheTextIsIncomplete(logger as Test.Logger) as Boolean {
+ var full = AegisFixtures.export(3, [AegisFixtures.totp("A", "a", AegisFixtures.SECRET_1), AegisFixtures.totp("B", "b", AegisFixtures.SECRET_2)], "");
+ return AegisFixtures.status(full.substring(0, full.length() / 2) as String) == AegisReader.INCOMPLETE
+ && AegisFixtures.status(full) == AegisReader.DONE;
+}
diff --git a/test/import/FuzzTest.mc b/test/import/FuzzTest.mc
new file mode 100644
index 0000000..7573ffa
--- /dev/null
+++ b/test/import/FuzzTest.mc
@@ -0,0 +1,107 @@
+// Author: cirakg & claude ai
+// Summary: Runs the seeded fuzz corpus through the tokenizer, the Aegis reader and the importer: always a result or a typed error, never a crash.
+//
+// README 17.3. Each input must end in a definite state within a bounded
+// number of steps (no endless loop), and anything the importer stores must
+// be a valid entry. Watchdog limits aren't enforced in unit tests; the
+// per-step bounds are covered by the UI flow on the Forerunner 255S.
+
+import Toybox.Lang;
+import Toybox.Test;
+
+(:test)
+function fuzzTokenizerAlwaysTerminates(logger as Test.Logger) as Boolean {
+ var inputs = FuzzCorpus.JSON;
+ for (var i = 0; i < inputs.size(); i++) {
+ var data = Bytes.fromHex(inputs[i] as String);
+ var tok = new Json.Tokenizer(data);
+ var finished = false;
+ // Every token consumes at least one byte, so size + 2 calls suffice.
+ for (var n = 0; n < data.size() + 2; n++) {
+ var t = tok.next();
+ if (t == Json.T_END || t == Json.T_INCOMPLETE || t == Json.T_ERROR) {
+ finished = true;
+ break;
+ }
+ }
+ if (!finished) {
+ logger.error("tokenizer didn't finish on input " + i);
+ return false;
+ }
+ }
+ return true;
+}
+
+(:test)
+function fuzzAegisReaderGivesDefiniteResults(logger as Test.Logger) as Boolean {
+ var inputs = FuzzCorpus.JSON;
+ var done = 0;
+ for (var i = 0; i < inputs.size(); i++) {
+ var data = Bytes.fromHex(inputs[i] as String);
+ var reader = new AegisReader(data);
+ var status = AegisReader.RUNNING;
+ for (var n = 0; n < data.size() + 2 && status == AegisReader.RUNNING; n++) {
+ status = reader.step();
+ }
+ if (status == AegisReader.RUNNING || (status == AegisReader.FAILED && reader.error == null)) {
+ logger.error("input " + i + ": status " + status);
+ return false;
+ }
+ if (status == AegisReader.DONE) {
+ done++;
+ for (var k = 0; k < reader.items.size(); k++) {
+ var item = reader.items[k];
+ var e = item.entry;
+ if (e == null ? item.problem == null && item.skipped == null : e.validate() != null) {
+ logger.error("input " + i + " item " + k + " is neither valid nor explained");
+ return false;
+ }
+ }
+ }
+ }
+ logger.debug("FUZZ aegis inputs=" + inputs.size() + " complete=" + done);
+ return done > 0;
+}
+
+// The whole importer on every input that is valid text: a report that
+// explains everything, and only valid entries in the vault.
+(:test)
+function fuzzImporterNeverStoresInvalidEntries(logger as Test.Logger) as Boolean {
+ var all = [] as Array<String>;
+ all.addAll(FuzzCorpus.JSON as Array<String>);
+ all.addAll(FuzzCorpus.URIS as Array<String>);
+ var imported = 0;
+ for (var i = 0; i < all.size(); i++) {
+ var text = Bytes.toUtf8(Bytes.fromHex(all[i]));
+ if (text == null) {
+ continue;
+ }
+ var vault = new Vault(new MemoryBackend(), new FakeTime());
+ var job = new Importer(vault, text, Importer.MODE_APPEND, false, 1800000000l);
+ var finished = false;
+ for (var n = 0; n < 5000; n++) {
+ if (job.step(1)) {
+ finished = true;
+ break;
+ }
+ }
+ var r = job.report;
+ if (!finished || (r.failure == null && r.waitingChars < 0 && r.summary().find("Imported") != 0)) {
+ logger.error("input " + i + " didn't finish cleanly: " + r.summary());
+ return false;
+ }
+ var ids = vault.ids();
+ for (var k = 0; k < ids.size(); k++) {
+ var e = vault.open(ids[k]);
+ var problem = e.validate();
+ e.wipeSecrets();
+ if (problem != null) {
+ logger.error("input " + i + " stored an invalid entry: " + problem);
+ return false;
+ }
+ }
+ imported += ids.size();
+ }
+ logger.debug("FUZZ importer inputs=" + all.size() + " entries_stored=" + imported);
+ return imported > 0;
+}
diff --git a/test/import/ImportSupport.mc b/test/import/ImportSupport.mc
new file mode 100644
index 0000000..ba891a7
--- /dev/null
+++ b/test/import/ImportSupport.mc
@@ -0,0 +1,76 @@
+// Author: cirakg & claude ai
+// Summary: Builders for fake Aegis exports and otpauth text used by the import tests (all secrets are RFC test keys).
+
+import Toybox.Lang;
+import Toybox.Test;
+
+(:test)
+module AegisFixtures {
+
+ const SECRET_1 = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"; // lint: allow-literal (RFC 4226 key "12345678901234567890")
+ const SECRET_2 = "JBSWY3DPEHPK3PXP"; // lint: allow-literal (the classic "Hello!" demo key)
+ const SECRET_3 = "MFRGGZDFMZTWQ2LKNNWG23TPOBYXE43U"; // lint: allow-literal (Base32 of "abcdefghijklmnopqrst")
+
+ // One Aegis entry. extra is inserted verbatim (other fields, icon...).
+ function entry(type as String, issuer as String, name as String, secret as String, algo as String, digits as Number,
+ period as Number, extra as String) as String {
+ return "{\"type\": \"" + type + "\", \"uuid\": \"0000\", \"name\": \"" + name + "\", \"issuer\": \"" + issuer
+ + "\", \"note\": \"private note\", " + extra
+ + "\"info\": {\"secret\": \"" + secret + "\", \"algo\": \"" + algo + "\", \"digits\": " + digits
+ + ", \"period\": " + period + "}}";
+ }
+
+ function totp(issuer as String, name as String, secret as String) as String {
+ return entry("totp", issuer, name, secret, "SHA1", 6, 30, "");
+ }
+
+ // A whole unencrypted export (vault version 1) of the given database
+ // version, with entries joined by commas and groups after the entries.
+ function export(dbVersion as Number, entries as Array<String>, groups as String) as String {
+ var joined = "";
+ for (var i = 0; i < entries.size(); i++) {
+ joined += (i > 0 ? ",\n" : "") + entries[i];
+ }
+ return "{\"version\": 1, \"header\": {\"slots\": null, \"params\": null}, \"db\": {\"version\": " + dbVersion
+ + ", \"entries\": [\n" + joined + "\n]" + (groups.length() > 0 ? ", \"groups\": " + groups : "") + "}}";
+ }
+
+ // Runs the reader to the end (or a non-running status).
+ function read(text as String) as AegisReader {
+ var reader = new AegisReader(Bytes.fromUtf8(text));
+ for (var i = 0; i < 10000; i++) {
+ if (reader.step() != AegisReader.RUNNING) {
+ return reader;
+ }
+ }
+ return reader;
+ }
+
+ function status(text as String) as Number {
+ var reader = new AegisReader(Bytes.fromUtf8(text));
+ for (var i = 0; i < 10000; i++) {
+ var s = reader.step();
+ if (s != AegisReader.RUNNING) {
+ return s;
+ }
+ }
+ return -1;
+ }
+
+ // Runs an import to completion on vault and returns its report.
+ function runImport(vault as Vault, text as String, mode as Number, locked as Boolean, now as Long) as ImportReport {
+ var job = new Importer(vault, text, mode, locked, now);
+ Jobs.runToCompletion(job, 1);
+ return job.report;
+ }
+
+ // Some detail line contains needle.
+ function hasDetail(report as ImportReport, needle as String) as Boolean {
+ for (var i = 0; i < report.details.size(); i++) {
+ if (report.details[i].find(needle) != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/test/import/ImporterTest.mc b/test/import/ImporterTest.mc
new file mode 100644
index 0000000..411e528
--- /dev/null
+++ b/test/import/ImporterTest.mc
@@ -0,0 +1,208 @@
+// Author: cirakg & claude ai
+// Summary: Tests for the import job: reports, duplicates, replacements, locking, Replace mode, multi-part buffer, failures, a full store, 100 entries.
+
+import Toybox.Lang;
+import Toybox.System;
+import Toybox.Test;
+
+(:test)
+module ImporterFixtures {
+
+ const NOW = 1800000000l;
+
+ function uri(issuer as String, account as String, secret as String) as String {
+ return "otpauth://totp/" + issuer + ":" + account + "?secret=" + secret;
+ }
+
+ function vault() as Vault {
+ return new Vault(new MemoryBackend(), new FakeTime());
+ }
+}
+
+(:test)
+function importerStoresUrisAndReports(logger as Test.Logger) as Boolean {
+ var vault = ImporterFixtures.vault();
+ var text = ImporterFixtures.uri("GitHub", "alice", AegisFixtures.SECRET_1) + "\n"
+ + ImporterFixtures.uri("Mail", "bob", AegisFixtures.SECRET_2) + "\n"
+ + "otpauth://totp/Bad:carol?secret=" + AegisFixtures.SECRET_3 + "&digits=9\n"
+ + "otpauth://motp/Old:dan?secret=" + AegisFixtures.SECRET_3;
+ var report = AegisFixtures.runImport(vault, text, Importer.MODE_APPEND, false, ImporterFixtures.NOW);
+ var ids = vault.ids();
+ var first = vault.open(ids[0]);
+ var ok = first.name.equals("GitHub") && first.source == Entry.SOURCE_IMPORT && first.created == ImporterFixtures.NOW;
+ first.wipeSecrets();
+ var summary = report.summary();
+ if (!summary.equals("Imported 2 · 1 error · 1 skipped")) {
+ logger.error("summary: " + summary);
+ return false;
+ }
+ return ok && ids.size() == 2 && AegisFixtures.hasDetail(report, "URI 3 'Bad': 9 digits")
+ && AegisFixtures.hasDetail(report, "URI 4 'Old': skipped, mOTP");
+}
+
+// The same type and secret is a duplicate, also of a locked entry while the
+// watch is locked (fingerprints); same issuer and account with another
+// secret is imported and flagged.
+(:test)
+function importerFindsDuplicatesAndReplacements(logger as Test.Logger) as Boolean {
+ var vault = ImporterFixtures.vault();
+ StorageFixtures.setPin(vault, StorageFixtures.PIN);
+ var locked = StorageFixtures.totp("Vault");
+ locked.secret = Bytes.fromUtf8("12345678901234567890");
+ locked.algorithm = Hashes.SHA1;
+ locked.locked = true;
+ var lockedId = vault.add(locked);
+ vault.lock();
+ var first = AegisFixtures.runImport(vault, ImporterFixtures.uri("GitHub", "alice", AegisFixtures.SECRET_2),
+ Importer.MODE_APPEND, false, ImporterFixtures.NOW);
+ var text = ImporterFixtures.uri("GitHub", "alice", AegisFixtures.SECRET_2) + " " // duplicate of the first import
+ + ImporterFixtures.uri("Other", "x", AegisFixtures.SECRET_1) + " " // duplicate of the locked entry
+ + ImporterFixtures.uri("GitHub", "alice", AegisFixtures.SECRET_3) + " " // same names, new secret
+ + ImporterFixtures.uri("Twice", "y", AegisFixtures.SECRET_3); // duplicate within this import
+ var second = AegisFixtures.runImport(vault, text, Importer.MODE_APPEND, false, ImporterFixtures.NOW);
+ if (second.duplicates != 3 || second.imported != 1) {
+ logger.error(second.summary());
+ return false;
+ }
+ return first.imported == 1 && AegisFixtures.hasDetail(second, "duplicate of " + lockedId)
+ && AegisFixtures.hasDetail(second, "possible replacement for") && vault.ids().size() == 3;
+}
+
+// "Require PIN" locks the whole import; while locked they wait as pending.
+(:test)
+function importerLocksEntriesAsPending(logger as Test.Logger) as Boolean {
+ var vault = ImporterFixtures.vault();
+ StorageFixtures.setPin(vault, StorageFixtures.PIN);
+ vault.lock();
+ var report = AegisFixtures.runImport(vault, ImporterFixtures.uri("Bank", "eve", AegisFixtures.SECRET_1),
+ Importer.MODE_APPEND, true, ImporterFixtures.NOW);
+ return report.imported == 1 && report.pendingLocked == 1 && vault.pendingIds().size() == 1 && vault.ids().size() == 0
+ && report.summary().find("waiting for your PIN") != null;
+}
+
+// Replace mode deletes earlier imported entries, never manual ones.
+(:test)
+function importerReplaceModeKeepsManualEntries(logger as Test.Logger) as Boolean {
+ var vault = ImporterFixtures.vault();
+ var manualId = vault.add(StorageFixtures.totp("Manual"));
+ // (The manual fixture uses SECRET_1's bytes, so the imports use other secrets.)
+ AegisFixtures.runImport(vault, ImporterFixtures.uri("Old", "a", AegisFixtures.SECRET_2) + " "
+ + ImporterFixtures.uri("Older", "b", AegisFixtures.SECRET_3), Importer.MODE_APPEND, false, ImporterFixtures.NOW);
+ // The same secret as a replaced entry is not a duplicate any more.
+ var report = AegisFixtures.runImport(vault, ImporterFixtures.uri("New", "c", AegisFixtures.SECRET_2),
+ Importer.MODE_REPLACE, false, ImporterFixtures.NOW);
+ var ids = vault.ids();
+ var newest = vault.header(ids[1]);
+ return report.removed == 2 && report.imported == 1 && report.duplicates == 0 && ids.size() == 2
+ && ids[0].equals(manualId) && newest != null && newest.name.equals("New");
+}
+
+// JSON cut in two parts: the first waits (sealed in storage), the second
+// completes the import and clears the buffer.
+(:test)
+function importerJoinsMultiPartJson(logger as Test.Logger) as Boolean {
+ var vault = ImporterFixtures.vault();
+ var store = vault.storage() as MemoryBackend;
+ var full = AegisFixtures.export(3, [AegisFixtures.totp("A", "a", AegisFixtures.SECRET_1),
+ AegisFixtures.totp("B", "b", AegisFixtures.SECRET_2), AegisFixtures.totp("C", "c", AegisFixtures.SECRET_3)], "");
+ var cut = full.length() / 2;
+ var part1 = AegisFixtures.runImport(vault, full.substring(0, cut) as String, Importer.MODE_APPEND, false, ImporterFixtures.NOW);
+ var waiting = part1.waitingChars == cut && ImportBuffer.hasPending(store) && vault.ids().size() == 0
+ && part1.summary().find("Waiting for more") != null;
+ // The buffer is sealed: the secret's Base32 text isn't readable in storage.
+ var chunk = store.raw("imp.0") as ByteArray;
+ var sealed = Bytes.toHex(chunk).find(Bytes.toHex(Bytes.fromUtf8(AegisFixtures.SECRET_1))) == null;
+ var part2 = AegisFixtures.runImport(vault, full.substring(cut, full.length()) as String, Importer.MODE_APPEND, false,
+ ImporterFixtures.NOW + 60);
+ return waiting && sealed && part2.imported == 3 && !ImportBuffer.hasPending(store);
+}
+
+(:test)
+function importerDiscardsExpiredParts(logger as Test.Logger) as Boolean {
+ var vault = ImporterFixtures.vault();
+ var full = AegisFixtures.export(3, [AegisFixtures.totp("A", "a", AegisFixtures.SECRET_1)], "");
+ var cut = full.length() / 2;
+ AegisFixtures.runImport(vault, full.substring(0, cut) as String, Importer.MODE_APPEND, false, ImporterFixtures.NOW);
+ var late = AegisFixtures.runImport(vault, full.substring(cut, full.length()) as String, Importer.MODE_APPEND, false,
+ ImporterFixtures.NOW + ImportBuffer.MAX_AGE_S + 1);
+ return late.failure != null && vault.ids().size() == 0 && !ImportBuffer.hasPending(vault.storage());
+}
+
+(:test)
+function importerExplainsUnusableText(logger as Test.Logger) as Boolean {
+ var cases = [
+ ["", "empty"],
+ ["hello world", "not recognised"],
+ ["otpauth-migration://offline?data=CjEKCkhlbGxvId6tvu8", "Google Authenticator"],
+ ["{\"version\": 1, \"header\": {\"slots\": [1]}, \"db\": \"abc\"}", "encrypted"],
+ ["{\"version\": 1, \"db\": [1,]}", "not valid JSON"]
+ ];
+ for (var i = 0; i < cases.size(); i++) {
+ var c = cases[i] as Array<String>;
+ var report = AegisFixtures.runImport(ImporterFixtures.vault(), c[0], Importer.MODE_APPEND, false, ImporterFixtures.NOW);
+ var failure = report.failure;
+ if (failure == null || failure.find(c[1]) == null) {
+ logger.error("case " + i + ": " + failure);
+ return false;
+ }
+ }
+ return true;
+}
+
+// A store that fills up mid-import keeps the whole entries stored so far.
+(:test)
+function importerStopsCleanlyWhenTheStoreIsFull(logger as Test.Logger) as Boolean {
+ var vault = ImporterFixtures.vault();
+ var store = vault.storage() as MemoryBackend;
+ vault.add(StorageFixtures.totp("Existing"));
+ store.capacity = store.usedBytes() + 500;
+ var text = "";
+ for (var i = 0; i < 10; i++) {
+ text += ImporterFixtures.uri("E" + i, "a", Base32.encode(Bytes.fromUtf8("fake" + (200000 + i).toString()))) + " ";
+ }
+ var report = AegisFixtures.runImport(vault, text, Importer.MODE_APPEND, false, ImporterFixtures.NOW);
+ var reopened = new Vault(store, new FakeTime());
+ logger.debug("full store: " + report.summary() + " " + report.details.toString());
+ return report.imported >= 1 && report.imported < 10 && AegisFixtures.hasDetail(report, "Stopped")
+ && reopened.ids().size() == 1 + report.imported;
+}
+
+// Fingerprints: same type and secret give the same 8 bytes, anything else
+// differs; they are in the header and survive a round trip.
+(:test)
+function importerFingerprintsIdentifySecrets(logger as Test.Logger) as Boolean {
+ var vault = ImporterFixtures.vault();
+ var a = StorageFixtures.totp("A");
+ var b = StorageFixtures.totp("B");
+ var c = StorageFixtures.hotp("C", 0l);
+ var d = StorageFixtures.totp("D");
+ d.secret = Bytes.fromUtf8("another secret");
+ var fa = vault.keyManager.fingerprint(a);
+ var id = vault.add(a);
+ var header = vault.header(id);
+ return fa.size() == 8 && Bytes.equal(fa, vault.keyManager.fingerprint(b))
+ && !Bytes.equal(fa, vault.keyManager.fingerprint(c)) && !Bytes.equal(fa, vault.keyManager.fingerprint(d))
+ && header != null && Bytes.equal(header.fingerprint as ByteArray, fa);
+}
+
+// 100 entries from one Aegis export: all stored, timed for PERFORMANCE.md.
+(:test)
+function importerHandlesOneHundredEntries(logger as Test.Logger) as Boolean {
+ var entries = [] as Array<String>;
+ for (var i = 0; i < 100; i++) {
+ // Distinct fake secrets: Base32 of a counter-based 10-byte value.
+ var raw = Bytes.fromUtf8("fake" + (100000 + i).toString());
+ entries.add(AegisFixtures.totp("Issuer " + i, "user" + i + "@example.com", Base32.encode(raw)));
+ }
+ var text = AegisFixtures.export(3, entries, "");
+ var vault = ImporterFixtures.vault();
+ var start = System.getTimer();
+ var job = new Importer(vault, text, Importer.MODE_APPEND, false, ImporterFixtures.NOW);
+ var steps = 0;
+ while (!job.step(1)) {
+ steps++;
+ }
+ var elapsed = System.getTimer() - start;
+ logger.debug("BENCH import_100_aegis ms=" + elapsed + " steps=" + steps + " json_bytes=" + text.length());
+ return job.report.imported == 100 && vault.ids().size() == 100;
+}
diff --git a/test/import/JsonTest.mc b/test/import/JsonTest.mc
new file mode 100644
index 0000000..c98aaa4
--- /dev/null
+++ b/test/import/JsonTest.mc
@@ -0,0 +1,128 @@
+// Author: cirakg & claude ai
+// Summary: Tests for the streaming JSON tokenizer: tokens, escapes, numbers, errors, incomplete input, limits.
+
+import Toybox.Lang;
+import Toybox.Test;
+
+(:test)
+module JsonFixtures {
+
+ // All tokens of text, as "kind:value" strings (values for keys, strings
+ // and numbers), ending with the final token.
+ function tokens(text as String) as Array<String> {
+ var tok = new Json.Tokenizer(Bytes.fromUtf8(text));
+ var out = [] as Array<String>;
+ for (var i = 0; i < 200; i++) {
+ var t = tok.next();
+ if (t == Json.T_KEY) {
+ out.add("K:" + tok.text);
+ } else if (t == Json.T_STRING) {
+ out.add("S:" + tok.text);
+ } else if (t == Json.T_NUMBER) {
+ out.add(tok.numberIsInteger ? "N:" + tok.number : "N:float");
+ } else {
+ out.add("T" + t);
+ }
+ if (t == Json.T_END || t == Json.T_INCOMPLETE || t == Json.T_ERROR) {
+ return out;
+ }
+ }
+ return out;
+ }
+
+ function last(text as String) as Number {
+ var tok = new Json.Tokenizer(Bytes.fromUtf8(text));
+ for (var i = 0; i < 500; i++) {
+ var t = tok.next();
+ if (t == Json.T_END || t == Json.T_INCOMPLETE || t == Json.T_ERROR) {
+ return t;
+ }
+ }
+ return -1;
+ }
+}
+
+(:test)
+function jsonTokenizesAllValueKinds(logger as Test.Logger) as Boolean {
+ var got = JsonFixtures.tokens("{\"a\": [1, -20, true, false, null, \"x\\\"y\\n\\u00e9\\ud83d\\ude00\"], \"b\": {}}");
+ var expected = ["T1", "K:a", "T3", "N:1", "N:-20", "T8", "T9", "T10", "S:x\"y\né😀", "T4", "K:b", "T1", "T2", "T2", "T0"];
+ if (!got.toString().equals(expected.toString())) {
+ logger.error("got " + got.toString());
+ return false;
+ }
+ return true;
+}
+
+(:test)
+function jsonNumbers(logger as Test.Logger) as Boolean {
+ var got = JsonFixtures.tokens("[0, 1.5, 1e3, -0, 123456789012345678901, 999999999999999999]");
+ var expected = ["T3", "N:0", "N:float", "N:float", "N:0", "N:float", "N:999999999999999999", "T4", "T0"];
+ if (!got.toString().equals(expected.toString())) {
+ logger.error("got " + got.toString());
+ return false;
+ }
+ return true;
+}
+
+(:test)
+function jsonRejectsInvalidDocuments(logger as Test.Logger) as Boolean {
+ var bad = [
+ "[1,]", "{\"a\" 1}", "[01]", "[\"\\x\"]", "[\"\\ud800\"]", "[\"\\udc00\"]", "{a: 1}", "[1] [2]",
+ "[\"\\u0000\"]", "[1.]", "[-]", "[tru]", "{\"a\":1,}", "[\"a\tb\"]", "[1 2]", "]"
+ ];
+ for (var i = 0; i < bad.size(); i++) {
+ if (JsonFixtures.last(bad[i] as String) != Json.T_ERROR) {
+ logger.error("accepted invalid JSON " + bad[i]);
+ return false;
+ }
+ }
+ return true;
+}
+
+// Input that stops inside the document is "incomplete", not an error, so
+// a multi-part import can wait for the rest.
+(:test)
+function jsonReportsIncompleteInput(logger as Test.Logger) as Boolean {
+ var partial = ["{", "{\"a\"", "{\"a\":", "{\"a\": \"abc", "{\"a\": 12", "[tr", "[1, {\"b\": [", "{\"a\": \"x\\"];
+ for (var i = 0; i < partial.size(); i++) {
+ if (JsonFixtures.last(partial[i] as String) != Json.T_INCOMPLETE) {
+ logger.error("not incomplete: " + partial[i]);
+ return false;
+ }
+ }
+ return JsonFixtures.last("") == Json.T_INCOMPLETE && JsonFixtures.last(" 7 ") == Json.T_END;
+}
+
+(:test)
+function jsonEnforcesTheDepthLimit(logger as Test.Logger) as Boolean {
+ var ok = "";
+ var tooDeep = "";
+ for (var i = 0; i < Json.MAX_DEPTH; i++) {
+ ok += "[";
+ }
+ tooDeep = ok + "[";
+ var close = "";
+ for (var i = 0; i < Json.MAX_DEPTH; i++) {
+ close += "]";
+ }
+ return JsonFixtures.last(ok + close) == Json.T_END && JsonFixtures.last(tooDeep + close + "]") == Json.T_ERROR;
+}
+
+// Long strings (icons) are skipped without decoding, including escaped
+// quotes inside them; long keys are errors.
+(:test)
+function jsonSkipsLongStrings(logger as Test.Logger) as Boolean {
+ var big = "";
+ for (var i = 0; i < 300; i++) {
+ big += "ABCDEFGHIJ";
+ }
+ var withQuote = big + "\\\"" + big;
+ var got = JsonFixtures.tokens("{\"icon\": \"" + big + "\", \"q\": \"" + withQuote + "\", \"n\": \"ok\"}");
+ var expected = ["T1", "K:icon", "T11", "K:q", "T11", "K:n", "S:ok", "T2", "T0"];
+ if (!got.toString().equals(expected.toString())) {
+ logger.error("got " + got.toString());
+ return false;
+ }
+ return JsonFixtures.last("{\"" + big + "\": 1}") == Json.T_ERROR
+ && JsonFixtures.last("{\"icon\": \"" + big) == Json.T_INCOMPLETE;
+}
diff --git a/test/import/OtpAuthTest.mc b/test/import/OtpAuthTest.mc
new file mode 100644
index 0000000..67f43f5
--- /dev/null
+++ b/test/import/OtpAuthTest.mc
@@ -0,0 +1,99 @@
+// Author: cirakg & claude ai
+// Summary: Tests for otpauth:// parsing: splitting, labels, parameters, Steam, separators and every validation error.
+
+import Toybox.Lang;
+import Toybox.Test;
+
+(:test)
+module OtpAuthFixtures {
+
+ // RFC 4226 test key "12345678901234567890" in Base32.
+ const SECRET = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"; // lint: allow-literal (RFC test key)
+
+ function parse(uri as String) as ImportItem {
+ return OtpAuth.parse(uri, 1);
+ }
+
+ function entry(logger as Test.Logger, uri as String) as Entry? {
+ var item = parse(uri);
+ if (item.entry == null) {
+ logger.error("no entry for " + uri + ": " + item.problem + " / " + item.skipped);
+ }
+ return item.entry;
+ }
+}
+
+(:test)
+function otpauthSplitsRunTogetherUris(logger as Test.Logger) as Boolean {
+ var s = OtpAuthFixtures.SECRET;
+ var uris = OtpAuth.splitUris("junk otpauth://totp/A?secret=" + s + " \n OTPAUTH://totp/B?secret=" + s
+ + "otpauth://hotp/C?secret=" + s + "&counter=1\n");
+ return uris.size() == 3 && (uris[0] as String).find("totp/A") != null && (uris[1] as String).find("totp/B") != null
+ && (uris[2] as String).find("counter=1") != null && (uris[2] as String).find("\n") == null;
+}
+
+(:test)
+function otpauthLabelsAndDefaults(logger as Test.Logger) as Boolean {
+ var e = OtpAuthFixtures.entry(logger, "otpauth://totp/ACME%20Co:john%40example.com?secret=" + OtpAuthFixtures.SECRET);
+ var bare = OtpAuthFixtures.entry(logger, "otpauth://totp/alice?secret=" + OtpAuthFixtures.SECRET.toLower());
+ if (e == null || bare == null) {
+ return false;
+ }
+ return e.name.equals("ACME Co") && e.account.equals("john@example.com") && e.algorithm == Hashes.SHA1
+ && e.digits == 6 && e.period == 30 && e.type == Entry.OTP_TOTP && e.source == Entry.SOURCE_IMPORT
+ && Bytes.equal(e.secret as ByteArray, Bytes.fromUtf8("12345678901234567890"))
+ && bare.name.equals("") && bare.account.equals("alice");
+}
+
+(:test)
+function otpauthParametersAndSeparators(logger as Test.Logger) as Boolean {
+ var s = OtpAuthFixtures.SECRET;
+ var e = OtpAuthFixtures.entry(logger,
+ "otpauth://totp/Label:bob?SECRET=" + s + ";Algorithm=sha512;digits=8&period=60&image=https%3A%2F%2Fx&issuer=Big+Bank");
+ var item = OtpAuthFixtures.parse("otpauth://totp/Old:bob?secret=" + s + "&issuer=New");
+ if (e == null) {
+ return false;
+ }
+ return e.name.equals("Big Bank") && e.algorithm == Hashes.SHA512 && e.digits == 8 && e.period == 60
+ && item.entry != null && (item.entry as Entry).name.equals("New") && item.note != null
+ && e.account.equals("bob");
+}
+
+(:test)
+function otpauthHotpAndSteam(logger as Test.Logger) as Boolean {
+ var s = OtpAuthFixtures.SECRET;
+ var h = OtpAuthFixtures.entry(logger, "otpauth://hotp/VPN:dave?secret=" + s + "&counter=42");
+ var noCounter = OtpAuthFixtures.parse("otpauth://hotp/VPN:dave?secret=" + s);
+ var steam = OtpAuthFixtures.entry(logger, "otpauth://steam/Steam:erin?secret=" + s);
+ var encoder = OtpAuthFixtures.entry(logger, "otpauth://totp/Steam:erin?secret=" + s + "&encoder=steam&digits=5");
+ return h != null && h.type == Entry.OTP_HOTP && h.counter == 42l
+ && noCounter.problem != null && (noCounter.problem as String).find("counter") != null
+ && steam != null && steam.type == Entry.OTP_STEAM && encoder != null && encoder.type == Entry.OTP_STEAM;
+}
+
+(:test)
+function otpauthReportsEveryProblem(logger as Test.Logger) as Boolean {
+ var s = OtpAuthFixtures.SECRET;
+ var cases = [
+ ["otpauth://totp/A:b", "secret missing"],
+ ["otpauth://totp/A:b?secret=" + s + "&digits=9", "9 digits"],
+ ["otpauth://totp/A:b?secret=" + s + "&digits=x", "not a whole number"],
+ ["otpauth://totp/A:b?secret=" + s + "&period=0", "period 0"],
+ ["otpauth://totp/A:b?secret=" + s + "&algorithm=MD5", "algorithm"],
+ ["otpauth://totp/A:b?secret=ABC1DEFG", "position 4"],
+ ["otpauth://totp/A%2:b?secret=" + s, "% escape"],
+ ["otpauth://totp/?secret=" + s, "issuer or an account"],
+ ["otpauth://totp", "no '/'"]
+ ];
+ for (var i = 0; i < cases.size(); i++) {
+ var c = cases[i] as Array<String>;
+ var item = OtpAuthFixtures.parse(c[0]);
+ var problem = item.problem;
+ if (item.entry != null || problem == null || problem.find(c[1]) == null) {
+ logger.error("case " + i + ": problem " + problem);
+ return false;
+ }
+ }
+ var motp = OtpAuthFixtures.parse("otpauth://motp/A:b?secret=" + s);
+ return motp.skipped != null && motp.entry == null;
+}