Highly ambitious ATProtocol AppView service and sdks
at main 216 lines 6.5 kB view raw
1/** 2 * Tests for AT Protocol Lexicon Validation Library 3 * 4 * Comprehensive test suite covering all validation functionality including 5 * WASM function tests, lexicon validation, record validation, and resource management. 6 */ 7 8import { 9 LexiconValidator, 10 ValidationError, 11 validate, 12 validateRecord, 13 validateStringFormat, 14 validateWithDetails, 15 withValidator, 16 isValidNsid, 17 type LexiconDoc, 18} from "./lexicon.ts"; 19 20// ============================================================================ 21// Test Data 22// ============================================================================ 23 24const validLexicon: LexiconDoc = { 25 id: "com.example.post", 26 lexicon: 1, 27 defs: { 28 main: { 29 type: "record", 30 key: "tid", 31 record: { 32 type: "object", 33 required: ["text"], 34 properties: { 35 text: { type: "string", maxLength: 300 }, 36 author: { type: "string", format: "did" }, 37 }, 38 }, 39 }, 40 }, 41}; 42 43const lexiconWithBlob: LexiconDoc = { 44 id: "com.example.profile", 45 lexicon: 1, 46 defs: { 47 main: { 48 type: "record", 49 key: "tid", 50 record: { 51 type: "object", 52 properties: { 53 avatar: { type: "blob" }, 54 displayName: { type: "string" }, 55 }, 56 }, 57 }, 58 }, 59}; 60 61const lexiconWithLocalRefs: LexiconDoc = { 62 id: "com.example.refs", 63 lexicon: 1, 64 defs: { 65 main: { 66 type: "record", 67 key: "tid", 68 record: { 69 type: "object", 70 properties: { 71 image: { type: "ref", ref: "#imageRef" }, 72 }, 73 }, 74 }, 75 imageRef: { 76 type: "object", 77 properties: { 78 url: { type: "string" }, 79 }, 80 }, 81 }, 82}; 83 84const invalidLexicon: LexiconDoc = { 85 id: "com.example.broken", 86 lexicon: 1, 87 defs: { 88 main: { 89 type: "record", 90 key: "tid", 91 // missing required "record" field 92 }, 93 }, 94}; 95 96// ============================================================================ 97// Basic WASM Function Tests 98// ============================================================================ 99 100Deno.test("isValidNsid - valid NSID", async () => { 101 const result = await isValidNsid("com.example.post"); 102 if (result !== true) throw new Error(`Expected true, got ${result}`); 103}); 104 105Deno.test("isValidNsid - invalid NSID", async () => { 106 const result = await isValidNsid("com.example"); 107 if (result !== false) throw new Error(`Expected false, got ${result}`); 108}); 109 110Deno.test("validateStringFormat - valid DID", async () => { 111 await validateStringFormat("did:plc:example123", "did"); 112}); 113 114Deno.test("validateStringFormat - invalid DID", async () => { 115 let threw = false; 116 try { 117 await validateStringFormat("not-a-did", "did"); 118 } catch (error) { 119 threw = true; 120 if (!(error instanceof ValidationError)) { 121 throw new Error("Should throw ValidationError"); 122 } 123 } 124 if (!threw) throw new Error("Should have thrown for invalid DID"); 125}); 126 127// ============================================================================ 128// Lexicon Validation Tests 129// ============================================================================ 130 131Deno.test("validate - valid lexicon", async () => { 132 const result = await validate([validLexicon]); 133 if (result !== null) throw new Error("Valid lexicon should return null"); 134}); 135 136Deno.test("validate - invalid lexicon", async () => { 137 const result = await validate([invalidLexicon]); 138 if (result === null) throw new Error("Invalid lexicon should return errors"); 139 if (!("com.example.broken" in result)) 140 throw new Error("Should contain broken lexicon ID"); 141 if (!Array.isArray(result["com.example.broken"])) 142 throw new Error("Errors should be array"); 143}); 144 145Deno.test("validate - blob type support", async () => { 146 const result = await validate([lexiconWithBlob]); 147 if (result !== null) 148 throw new Error("Lexicon with blob types should be valid"); 149}); 150 151Deno.test("validate - local reference resolution", async () => { 152 const result = await validate([lexiconWithLocalRefs]); 153 if (result !== null) 154 throw new Error("Lexicon with local refs should be valid"); 155}); 156 157Deno.test("validateWithDetails - comprehensive reporting", async () => { 158 const result = await validateWithDetails([invalidLexicon]); 159 if (result.valid !== false) throw new Error("Should be invalid"); 160 if (result.lexiconsWithErrors !== 1) 161 throw new Error("Should have 1 lexicon with errors"); 162 if (!("com.example.broken" in result.errors)) 163 throw new Error("Should contain broken lexicon"); 164}); 165 166Deno.test("validateWithDetails - valid lexicon", async () => { 167 const result = await validateWithDetails([validLexicon]); 168 if (result.valid !== true) throw new Error("Should be valid"); 169 if (result.totalErrors !== 0) throw new Error("Should have no errors"); 170}); 171 172// ============================================================================ 173// LexiconValidator Class Tests 174// ============================================================================ 175 176Deno.test("LexiconValidator - valid record", async () => { 177 const validator = await LexiconValidator.create([validLexicon]); 178 const record = { text: "Hello!", author: "did:plc:test" }; 179 validator.validateRecord("com.example.post", record); 180 validator.free(); 181}); 182 183Deno.test("LexiconValidator - invalid record", async () => { 184 const validator = await LexiconValidator.create([validLexicon]); 185 const record = { author: "did:plc:test" }; // missing required text 186 187 let threw = false; 188 try { 189 validator.validateRecord("com.example.post", record); 190 } catch (error) { 191 threw = true; 192 if (!(error instanceof ValidationError)) { 193 throw new Error("Should throw ValidationError"); 194 } 195 } 196 if (!threw) throw new Error("Should have thrown for missing required field"); 197 validator.free(); 198}); 199 200// ============================================================================ 201// Standalone Function Tests 202// ============================================================================ 203 204Deno.test("validateRecord standalone function", async () => { 205 const record = { text: "Test", author: "did:plc:test" }; 206 await validateRecord([validLexicon], "com.example.post", record); 207}); 208 209Deno.test("withValidator resource management", async () => { 210 const result = await withValidator([validLexicon], (validator) => { 211 const record = { text: "Test", author: "did:plc:test" }; 212 validator.validateRecord("com.example.post", record); 213 return "success"; 214 }); 215 if (result !== "success") throw new Error("Should return callback result"); 216});