Highly ambitious ATProtocol AppView service and sdks
at main 244 lines 7.1 kB view raw
1import { assertEquals, assertStringIncludes } from "jsr:@std/assert"; 2import { Project } from "ts-morph"; 3import { generateInterfaces } from "../src/interfaces.ts"; 4import type { Lexicon } from "../src/mod.ts"; 5 6function createTestProject() { 7 return new Project({ useInMemoryFileSystem: true }); 8} 9 10Deno.test("generateInterfaces - creates record interfaces", () => { 11 const project = createTestProject(); 12 const sourceFile = project.createSourceFile("test.ts", ""); 13 14 const lexicons: Lexicon[] = [ 15 { 16 id: "com.example.post", 17 definitions: { 18 main: { 19 type: "record", 20 record: { 21 type: "record", 22 properties: { 23 text: { type: "string" }, 24 createdAt: { type: "string", format: "datetime" }, 25 likes: { type: "integer" }, 26 }, 27 required: ["text", "createdAt"], 28 }, 29 }, 30 }, 31 }, 32 ]; 33 34 generateInterfaces(sourceFile, lexicons); 35 const result = sourceFile.getFullText(); 36 37 // Should create interface for the record 38 assertStringIncludes(result, "export interface ComExamplePost"); 39 assertStringIncludes(result, "text: string;"); 40 assertStringIncludes(result, "createdAt: string;"); 41 assertStringIncludes(result, "likes?: number;"); 42 43 // Should create sort fields type alias 44 assertStringIncludes(result, "export type ComExamplePostSortFields"); 45 assertStringIncludes(result, '"text" | "createdAt" | "likes"'); 46}); 47 48Deno.test("generateInterfaces - creates object interfaces", () => { 49 const project = createTestProject(); 50 const sourceFile = project.createSourceFile("test.ts", ""); 51 52 const lexicons: Lexicon[] = [ 53 { 54 id: "app.bsky.embed.defs", 55 definitions: { 56 aspectRatio: { 57 type: "object", 58 properties: { 59 width: { type: "integer" }, 60 height: { type: "integer" }, 61 }, 62 required: ["width", "height"], 63 }, 64 }, 65 }, 66 ]; 67 68 generateInterfaces(sourceFile, lexicons); 69 const result = sourceFile.getFullText(); 70 71 assertStringIncludes(result, "export interface AppBskyEmbedDefsAspectRatio"); 72 assertStringIncludes(result, "width: number;"); 73 assertStringIncludes(result, "height: number;"); 74}); 75 76Deno.test("generateInterfaces - creates union type aliases", () => { 77 const project = createTestProject(); 78 const sourceFile = project.createSourceFile("test.ts", ""); 79 80 const lexicons: Lexicon[] = [ 81 { 82 id: "app.bsky.embed.defs", 83 definitions: { 84 view: { 85 type: "union", 86 refs: ["#imageView", "#videoView"], 87 closed: false, 88 }, 89 imageView: { 90 type: "object", 91 properties: { alt: { type: "string" } }, 92 }, 93 videoView: { 94 type: "object", 95 properties: { duration: { type: "integer" } }, 96 }, 97 }, 98 }, 99 ]; 100 101 generateInterfaces(sourceFile, lexicons); 102 const result = sourceFile.getFullText(); 103 104 assertStringIncludes(result, "export type AppBskyEmbedDefsView"); 105 assertStringIncludes(result, "AppBskyEmbedDefs[\"ImageView\"]"); 106 assertStringIncludes(result, "AppBskyEmbedDefs[\"VideoView\"]"); 107 // Open union should include unknown type 108 assertStringIncludes(result, "{ $type: string; [key: string]: unknown }"); 109}); 110 111Deno.test("generateInterfaces - creates known values types", () => { 112 const project = createTestProject(); 113 const sourceFile = project.createSourceFile("test.ts", ""); 114 115 const lexicons: Lexicon[] = [ 116 { 117 id: "com.example.post", 118 definitions: { 119 main: { 120 type: "record", 121 record: { 122 type: "record", 123 properties: { 124 status: { 125 type: "string", 126 knownValues: ["draft", "published", "archived"], 127 }, 128 }, 129 }, 130 }, 131 }, 132 }, 133 ]; 134 135 generateInterfaces(sourceFile, lexicons); 136 const result = sourceFile.getFullText(); 137 138 assertStringIncludes(result, "export type ComExamplePostStatus"); 139 assertStringIncludes(result, "'draft'"); 140 assertStringIncludes(result, "'published'"); 141 assertStringIncludes(result, "'archived'"); 142 assertStringIncludes(result, "(string & Record<string, never>)"); 143}); 144 145Deno.test("generateInterfaces - creates namespace interfaces", () => { 146 const project = createTestProject(); 147 const sourceFile = project.createSourceFile("test.ts", ""); 148 149 const lexicons: Lexicon[] = [ 150 { 151 id: "app.bsky.embed.defs", 152 definitions: { 153 aspectRatio: { 154 type: "object", 155 properties: { width: { type: "integer" } }, 156 }, 157 view: { 158 type: "union", 159 refs: ["#aspectRatio"], 160 }, 161 }, 162 }, 163 ]; 164 165 generateInterfaces(sourceFile, lexicons); 166 const result = sourceFile.getFullText(); 167 168 // Should create namespace interface for multiple definitions 169 assertStringIncludes(result, "export interface AppBskyEmbedDefs"); 170 assertStringIncludes(result, "readonly AspectRatio: AppBskyEmbedDefsAspectRatio;"); 171 assertStringIncludes(result, "readonly View: AppBskyEmbedDefsView;"); 172}); 173 174Deno.test("generateInterfaces - generates from network.slices lexicons", () => { 175 const project = createTestProject(); 176 const sourceFile = project.createSourceFile("test.ts", ""); 177 178 const lexicons = [ 179 { 180 id: "network.slices.slice", 181 definitions: { 182 main: { 183 type: "record", 184 record: { 185 type: "record", 186 properties: { 187 name: { type: "string" }, 188 }, 189 required: ["name"], 190 }, 191 }, 192 }, 193 }, 194 ]; 195 196 generateInterfaces(sourceFile, lexicons); 197 const result = sourceFile.getFullText(); 198 199 assertStringIncludes(result, "export interface NetworkSlicesSlice"); 200 assertStringIncludes(result, "name: string;"); 201}); 202 203Deno.test("generateInterfaces - generates empty output for empty lexicons", () => { 204 const project = createTestProject(); 205 const sourceFile = project.createSourceFile("test.ts", ""); 206 207 generateInterfaces(sourceFile, []); 208 const result = sourceFile.getFullText(); 209 210 // Should have minimal output for empty lexicons 211 assertEquals(result.trim(), ""); 212}); 213 214Deno.test("generateInterfaces - handles single definition lexicons", () => { 215 const project = createTestProject(); 216 const sourceFile = project.createSourceFile("test.ts", ""); 217 218 const lexicons: Lexicon[] = [ 219 { 220 id: "com.atproto.repo.strongRef", 221 definitions: { 222 main: { 223 type: "object", 224 properties: { 225 uri: { type: "string" }, 226 cid: { type: "string" }, 227 }, 228 required: ["uri", "cid"], 229 }, 230 }, 231 }, 232 ]; 233 234 generateInterfaces(sourceFile, lexicons); 235 const result = sourceFile.getFullText(); 236 237 // Should use clean name for single definition 238 assertStringIncludes(result, "export interface ComAtprotoRepoStrongRef"); 239 assertStringIncludes(result, "uri: string;"); 240 assertStringIncludes(result, "cid: string;"); 241 242 // Should NOT create namespace interface for single definition 243 assertEquals(result.includes("readonly Main:"), false); 244});