forked from
slices.network/slices
Highly ambitious ATProtocol AppView service and sdks
1import { assertEquals, assertExists } from "jsr:@std/assert";
2import {
3 normalizeLexicons,
4 nsidToPascalCase,
5 nsidToNamespace,
6 defNameToPascalCase,
7 capitalizeFirst,
8 sanitizeIdentifier,
9 isPropertyRequired,
10 isFieldSortable,
11 createProject,
12 generateUsageExample,
13 generateHeaderComment,
14 type Lexicon,
15 type LexiconRecord,
16} from "../src/mod.ts";
17
18Deno.test("normalizeLexicons - handles both id and nsid properties", () => {
19 const input = [
20 { id: "app.bsky.actor.profile", definitions: {} },
21 { nsid: "com.example.post", defs: {} },
22 { id: "network.slices.slice", definitions: {} },
23 ];
24
25 const result = normalizeLexicons(input);
26
27 assertEquals(result[0].id, "app.bsky.actor.profile");
28 assertEquals(result[1].id, "com.example.post");
29 assertEquals(result[1].definitions, {});
30 assertEquals(result[2].id, "network.slices.slice");
31});
32
33Deno.test("nsidToPascalCase - converts NSID to PascalCase", () => {
34 assertEquals(nsidToPascalCase("app.bsky.actor.profile"), "AppBskyActorProfile");
35 assertEquals(nsidToPascalCase("com.example.my-post"), "ComExampleMyPost");
36 assertEquals(nsidToPascalCase("network.slices.slice"), "NetworkSlicesSlice");
37});
38
39Deno.test("nsidToNamespace - converts NSID to namespace format", () => {
40 assertEquals(nsidToNamespace("app.bsky.embed.defs"), "AppBskyEmbedDefs");
41 assertEquals(nsidToNamespace("com.example.test-case"), "ComExampleTestCase");
42});
43
44Deno.test("defNameToPascalCase - converts definition names", () => {
45 assertEquals(defNameToPascalCase("aspectRatio"), "AspectRatio");
46 assertEquals(defNameToPascalCase("my-property"), "MyProperty");
47 assertEquals(defNameToPascalCase("test_case"), "TestCase");
48});
49
50Deno.test("capitalizeFirst - capitalizes first letter", () => {
51 assertEquals(capitalizeFirst("hello"), "Hello");
52 assertEquals(capitalizeFirst("world"), "World");
53 assertEquals(capitalizeFirst(""), "");
54});
55
56Deno.test("sanitizeIdentifier - converts hyphens to camelCase", () => {
57 assertEquals(sanitizeIdentifier("cyber-meteor-1637"), "cyberMeteor1637");
58 assertEquals(sanitizeIdentifier("test-name"), "testName");
59 assertEquals(sanitizeIdentifier("already-camel-case"), "alreadyCamelCase");
60 assertEquals(sanitizeIdentifier("single"), "single");
61 assertEquals(sanitizeIdentifier("with_underscore"), "withUnderscore");
62 assertEquals(sanitizeIdentifier("mixed-name_test"), "mixedNameTest");
63});
64
65Deno.test("isPropertyRequired - checks if property is required", () => {
66 const record: LexiconRecord = {
67 type: "record",
68 required: ["name", "email"],
69 properties: {
70 name: { type: "string" },
71 email: { type: "string" },
72 age: { type: "integer" },
73 },
74 };
75
76 assertEquals(isPropertyRequired(record, "name"), true);
77 assertEquals(isPropertyRequired(record, "email"), true);
78 assertEquals(isPropertyRequired(record, "age"), false);
79});
80
81Deno.test("isFieldSortable - identifies sortable field types", () => {
82 assertEquals(isFieldSortable({ type: "string" }), true);
83 assertEquals(isFieldSortable({ type: "integer" }), true);
84 assertEquals(isFieldSortable({ type: "number" }), true);
85 assertEquals(isFieldSortable({ type: "datetime" }), true);
86 assertEquals(isFieldSortable({ type: "string", format: "datetime" }), true);
87 assertEquals(isFieldSortable({ type: "string", format: "at-identifier" }), true);
88 assertEquals(isFieldSortable({ type: "string", format: "at-uri" }), true);
89
90 assertEquals(isFieldSortable({ type: "array" }), false);
91 assertEquals(isFieldSortable({ type: "object" }), false);
92 assertEquals(isFieldSortable({ type: "blob" }), false);
93});
94
95Deno.test("createProject - creates ts-morph project", () => {
96 const project = createProject();
97 assertExists(project);
98 assertEquals(typeof project.createSourceFile, "function");
99});
100
101Deno.test("generateUsageExample - creates usage documentation", () => {
102 const lexicons: Lexicon[] = [
103 {
104 id: "com.example.post",
105 definitions: {
106 main: {
107 type: "record",
108 record: {
109 type: "record",
110 properties: {
111 text: { type: "string" },
112 },
113 },
114 },
115 },
116 },
117 ];
118
119 const result = generateUsageExample(lexicons, "at://test/slice");
120
121 assertEquals(typeof result, "string");
122 assertEquals(result.includes("com.example.post"), true);
123 assertEquals(result.includes("at://test/slice"), true);
124 assertEquals(result.includes("client.com.example.post.getRecords()"), true);
125});
126
127Deno.test("generateUsageExample - handles network.slices lexicons", () => {
128 const lexicons: Lexicon[] = [
129 {
130 id: "network.slices.slice",
131 definitions: {
132 main: {
133 type: "record",
134 record: {
135 type: "record",
136 properties: {
137 name: { type: "string" },
138 },
139 },
140 },
141 },
142 },
143 ];
144
145 const result = generateUsageExample(lexicons, "at://test/slice");
146
147 assertEquals(typeof result, "string");
148 // Should fallback to using network.slices lexicons when no other record types exist
149 assertEquals(result.includes("network.slices.slice"), true);
150});
151
152Deno.test("generateHeaderComment - creates header with imports", () => {
153 const lexicons: Lexicon[] = [
154 { id: "test.lexicon", definitions: {} },
155 ];
156
157 const usageExample = "/** Example usage */";
158 const result = generateHeaderComment(lexicons, usageExample);
159
160 assertEquals(typeof result, "string");
161 assertEquals(result.includes("Generated TypeScript client"), true);
162 assertEquals(result.includes("Lexicons: 1"), true);
163 assertEquals(result.includes(usageExample), true);
164 assertEquals(result.includes("@slices/client"), true);
165 assertEquals(result.includes("@slices/oauth"), true);
166});
167
168Deno.test("generateHeaderComment - generates consistent imports", () => {
169 const lexicons: Lexicon[] = [];
170 const usageExample = "/** Example */";
171 const result = generateHeaderComment(lexicons, usageExample);
172
173 assertEquals(result.includes("SlicesClient"), true);
174 assertEquals(result.includes("@slices/client"), true);
175 assertEquals(result.includes("@slices/oauth"), true);
176});