@debriefer/sources 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/README.md +59 -0
  2. package/dist/__tests__/archives/chronicling-america.test.d.ts +8 -0
  3. package/dist/__tests__/archives/chronicling-america.test.d.ts.map +1 -0
  4. package/dist/__tests__/archives/chronicling-america.test.js +151 -0
  5. package/dist/__tests__/archives/chronicling-america.test.js.map +1 -0
  6. package/dist/__tests__/archives/europeana.test.d.ts +8 -0
  7. package/dist/__tests__/archives/europeana.test.d.ts.map +1 -0
  8. package/dist/__tests__/archives/europeana.test.js +200 -0
  9. package/dist/__tests__/archives/europeana.test.js.map +1 -0
  10. package/dist/__tests__/archives/internet-archive.test.d.ts +8 -0
  11. package/dist/__tests__/archives/internet-archive.test.d.ts.map +1 -0
  12. package/dist/__tests__/archives/internet-archive.test.js +189 -0
  13. package/dist/__tests__/archives/internet-archive.test.js.map +1 -0
  14. package/dist/__tests__/archives/trove.test.d.ts +8 -0
  15. package/dist/__tests__/archives/trove.test.d.ts.map +1 -0
  16. package/dist/__tests__/archives/trove.test.js +202 -0
  17. package/dist/__tests__/archives/trove.test.js.map +1 -0
  18. package/dist/__tests__/books/google-books.test.d.ts +8 -0
  19. package/dist/__tests__/books/google-books.test.d.ts.map +1 -0
  20. package/dist/__tests__/books/google-books.test.js +221 -0
  21. package/dist/__tests__/books/google-books.test.js.map +1 -0
  22. package/dist/__tests__/books/open-library.test.d.ts +8 -0
  23. package/dist/__tests__/books/open-library.test.d.ts.map +1 -0
  24. package/dist/__tests__/books/open-library.test.js +159 -0
  25. package/dist/__tests__/books/open-library.test.js.map +1 -0
  26. package/dist/__tests__/news/guardian.test.d.ts +9 -0
  27. package/dist/__tests__/news/guardian.test.d.ts.map +1 -0
  28. package/dist/__tests__/news/guardian.test.js +224 -0
  29. package/dist/__tests__/news/guardian.test.js.map +1 -0
  30. package/dist/__tests__/news/nytimes.test.d.ts +9 -0
  31. package/dist/__tests__/news/nytimes.test.d.ts.map +1 -0
  32. package/dist/__tests__/news/nytimes.test.js +271 -0
  33. package/dist/__tests__/news/nytimes.test.js.map +1 -0
  34. package/dist/__tests__/news/site-search-source.test.d.ts +9 -0
  35. package/dist/__tests__/news/site-search-source.test.d.ts.map +1 -0
  36. package/dist/__tests__/news/site-search-source.test.js +342 -0
  37. package/dist/__tests__/news/site-search-source.test.js.map +1 -0
  38. package/dist/__tests__/obituary/find-a-grave.test.d.ts +8 -0
  39. package/dist/__tests__/obituary/find-a-grave.test.d.ts.map +1 -0
  40. package/dist/__tests__/obituary/find-a-grave.test.js +238 -0
  41. package/dist/__tests__/obituary/find-a-grave.test.js.map +1 -0
  42. package/dist/__tests__/shared/duckduckgo-search.test.d.ts +9 -0
  43. package/dist/__tests__/shared/duckduckgo-search.test.d.ts.map +1 -0
  44. package/dist/__tests__/shared/duckduckgo-search.test.js +218 -0
  45. package/dist/__tests__/shared/duckduckgo-search.test.js.map +1 -0
  46. package/dist/__tests__/shared/fetch-page.test.d.ts +9 -0
  47. package/dist/__tests__/shared/fetch-page.test.d.ts.map +1 -0
  48. package/dist/__tests__/shared/fetch-page.test.js +281 -0
  49. package/dist/__tests__/shared/fetch-page.test.js.map +1 -0
  50. package/dist/__tests__/shared/html-utils.test.d.ts +2 -0
  51. package/dist/__tests__/shared/html-utils.test.d.ts.map +1 -0
  52. package/dist/__tests__/shared/html-utils.test.js +169 -0
  53. package/dist/__tests__/shared/html-utils.test.js.map +1 -0
  54. package/dist/__tests__/shared/readability-extract.test.d.ts +2 -0
  55. package/dist/__tests__/shared/readability-extract.test.d.ts.map +1 -0
  56. package/dist/__tests__/shared/readability-extract.test.js +107 -0
  57. package/dist/__tests__/shared/readability-extract.test.js.map +1 -0
  58. package/dist/__tests__/shared/sanitize-text.test.d.ts +2 -0
  59. package/dist/__tests__/shared/sanitize-text.test.d.ts.map +1 -0
  60. package/dist/__tests__/shared/sanitize-text.test.js +77 -0
  61. package/dist/__tests__/shared/sanitize-text.test.js.map +1 -0
  62. package/dist/__tests__/shared/search-utils.test.d.ts +2 -0
  63. package/dist/__tests__/shared/search-utils.test.d.ts.map +1 -0
  64. package/dist/__tests__/shared/search-utils.test.js +26 -0
  65. package/dist/__tests__/shared/search-utils.test.js.map +1 -0
  66. package/dist/__tests__/structured/wikidata.test.d.ts +9 -0
  67. package/dist/__tests__/structured/wikidata.test.d.ts.map +1 -0
  68. package/dist/__tests__/structured/wikidata.test.js +509 -0
  69. package/dist/__tests__/structured/wikidata.test.js.map +1 -0
  70. package/dist/__tests__/structured/wikipedia.test.d.ts +9 -0
  71. package/dist/__tests__/structured/wikipedia.test.d.ts.map +1 -0
  72. package/dist/__tests__/structured/wikipedia.test.js +643 -0
  73. package/dist/__tests__/structured/wikipedia.test.js.map +1 -0
  74. package/dist/__tests__/web-search/base.test.d.ts +9 -0
  75. package/dist/__tests__/web-search/base.test.d.ts.map +1 -0
  76. package/dist/__tests__/web-search/base.test.js +622 -0
  77. package/dist/__tests__/web-search/base.test.js.map +1 -0
  78. package/dist/__tests__/web-search/bing.test.d.ts +10 -0
  79. package/dist/__tests__/web-search/bing.test.d.ts.map +1 -0
  80. package/dist/__tests__/web-search/bing.test.js +277 -0
  81. package/dist/__tests__/web-search/bing.test.js.map +1 -0
  82. package/dist/__tests__/web-search/brave.test.d.ts +10 -0
  83. package/dist/__tests__/web-search/brave.test.d.ts.map +1 -0
  84. package/dist/__tests__/web-search/brave.test.js +264 -0
  85. package/dist/__tests__/web-search/brave.test.js.map +1 -0
  86. package/dist/__tests__/web-search/duckduckgo.test.d.ts +10 -0
  87. package/dist/__tests__/web-search/duckduckgo.test.d.ts.map +1 -0
  88. package/dist/__tests__/web-search/duckduckgo.test.js +107 -0
  89. package/dist/__tests__/web-search/duckduckgo.test.js.map +1 -0
  90. package/dist/__tests__/web-search/google.test.d.ts +9 -0
  91. package/dist/__tests__/web-search/google.test.d.ts.map +1 -0
  92. package/dist/__tests__/web-search/google.test.js +189 -0
  93. package/dist/__tests__/web-search/google.test.js.map +1 -0
  94. package/dist/archives/chronicling-america.d.ts +33 -0
  95. package/dist/archives/chronicling-america.d.ts.map +1 -0
  96. package/dist/archives/chronicling-america.js +85 -0
  97. package/dist/archives/chronicling-america.js.map +1 -0
  98. package/dist/archives/europeana.d.ts +37 -0
  99. package/dist/archives/europeana.d.ts.map +1 -0
  100. package/dist/archives/europeana.js +92 -0
  101. package/dist/archives/europeana.js.map +1 -0
  102. package/dist/archives/internet-archive.d.ts +32 -0
  103. package/dist/archives/internet-archive.d.ts.map +1 -0
  104. package/dist/archives/internet-archive.js +90 -0
  105. package/dist/archives/internet-archive.js.map +1 -0
  106. package/dist/archives/trove.d.ts +37 -0
  107. package/dist/archives/trove.d.ts.map +1 -0
  108. package/dist/archives/trove.js +97 -0
  109. package/dist/archives/trove.js.map +1 -0
  110. package/dist/books/google-books.d.ts +48 -0
  111. package/dist/books/google-books.d.ts.map +1 -0
  112. package/dist/books/google-books.js +111 -0
  113. package/dist/books/google-books.js.map +1 -0
  114. package/dist/books/open-library.d.ts +44 -0
  115. package/dist/books/open-library.d.ts.map +1 -0
  116. package/dist/books/open-library.js +103 -0
  117. package/dist/books/open-library.js.map +1 -0
  118. package/dist/index.d.ts +45 -0
  119. package/dist/index.d.ts.map +1 -0
  120. package/dist/index.js +35 -0
  121. package/dist/index.js.map +1 -0
  122. package/dist/news/guardian.d.ts +51 -0
  123. package/dist/news/guardian.d.ts.map +1 -0
  124. package/dist/news/guardian.js +131 -0
  125. package/dist/news/guardian.js.map +1 -0
  126. package/dist/news/nytimes.d.ts +27 -0
  127. package/dist/news/nytimes.d.ts.map +1 -0
  128. package/dist/news/nytimes.js +104 -0
  129. package/dist/news/nytimes.js.map +1 -0
  130. package/dist/news/site-search-source.d.ts +89 -0
  131. package/dist/news/site-search-source.d.ts.map +1 -0
  132. package/dist/news/site-search-source.js +182 -0
  133. package/dist/news/site-search-source.js.map +1 -0
  134. package/dist/news/sources.d.ts +52 -0
  135. package/dist/news/sources.d.ts.map +1 -0
  136. package/dist/news/sources.js +276 -0
  137. package/dist/news/sources.js.map +1 -0
  138. package/dist/obituary/find-a-grave.d.ts +43 -0
  139. package/dist/obituary/find-a-grave.d.ts.map +1 -0
  140. package/dist/obituary/find-a-grave.js +173 -0
  141. package/dist/obituary/find-a-grave.js.map +1 -0
  142. package/dist/shared/duckduckgo-search.d.ts +86 -0
  143. package/dist/shared/duckduckgo-search.d.ts.map +1 -0
  144. package/dist/shared/duckduckgo-search.js +218 -0
  145. package/dist/shared/duckduckgo-search.js.map +1 -0
  146. package/dist/shared/fetch-page.d.ts +50 -0
  147. package/dist/shared/fetch-page.d.ts.map +1 -0
  148. package/dist/shared/fetch-page.js +212 -0
  149. package/dist/shared/fetch-page.js.map +1 -0
  150. package/dist/shared/html-utils.d.ts +99 -0
  151. package/dist/shared/html-utils.d.ts.map +1 -0
  152. package/dist/shared/html-utils.js +246 -0
  153. package/dist/shared/html-utils.js.map +1 -0
  154. package/dist/shared/readability-extract.d.ts +33 -0
  155. package/dist/shared/readability-extract.d.ts.map +1 -0
  156. package/dist/shared/readability-extract.js +45 -0
  157. package/dist/shared/readability-extract.js.map +1 -0
  158. package/dist/shared/sanitize-text.d.ts +24 -0
  159. package/dist/shared/sanitize-text.d.ts.map +1 -0
  160. package/dist/shared/sanitize-text.js +49 -0
  161. package/dist/shared/sanitize-text.js.map +1 -0
  162. package/dist/shared/search-utils.d.ts +18 -0
  163. package/dist/shared/search-utils.d.ts.map +1 -0
  164. package/dist/shared/search-utils.js +20 -0
  165. package/dist/shared/search-utils.js.map +1 -0
  166. package/dist/structured/wikidata.d.ts +128 -0
  167. package/dist/structured/wikidata.d.ts.map +1 -0
  168. package/dist/structured/wikidata.js +361 -0
  169. package/dist/structured/wikidata.js.map +1 -0
  170. package/dist/structured/wikipedia.d.ts +184 -0
  171. package/dist/structured/wikipedia.d.ts.map +1 -0
  172. package/dist/structured/wikipedia.js +275 -0
  173. package/dist/structured/wikipedia.js.map +1 -0
  174. package/dist/web-search/base.d.ts +128 -0
  175. package/dist/web-search/base.d.ts.map +1 -0
  176. package/dist/web-search/base.js +251 -0
  177. package/dist/web-search/base.js.map +1 -0
  178. package/dist/web-search/bing.d.ts +21 -0
  179. package/dist/web-search/bing.d.ts.map +1 -0
  180. package/dist/web-search/bing.js +53 -0
  181. package/dist/web-search/bing.js.map +1 -0
  182. package/dist/web-search/brave.d.ts +21 -0
  183. package/dist/web-search/brave.d.ts.map +1 -0
  184. package/dist/web-search/brave.js +56 -0
  185. package/dist/web-search/brave.js.map +1 -0
  186. package/dist/web-search/duckduckgo.d.ts +15 -0
  187. package/dist/web-search/duckduckgo.d.ts.map +1 -0
  188. package/dist/web-search/duckduckgo.js +21 -0
  189. package/dist/web-search/duckduckgo.js.map +1 -0
  190. package/dist/web-search/google.d.ts +24 -0
  191. package/dist/web-search/google.d.ts.map +1 -0
  192. package/dist/web-search/google.js +48 -0
  193. package/dist/web-search/google.js.map +1 -0
  194. package/package.json +58 -0
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Tests for GoogleBooksSource.
3
+ *
4
+ * Mocks the global fetch API and sanitizeSourceText to isolate
5
+ * Google Books-specific logic.
6
+ */
7
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
8
+ import { ReliabilityTier } from "@debriefer/core";
9
+ // ============================================================================
10
+ // Mocks — must be declared before importing the module under test
11
+ // ============================================================================
12
+ const mockSanitize = vi.fn();
13
+ const mockDecodeEntities = vi.fn();
14
+ vi.mock("../../shared/sanitize-text.js", () => ({
15
+ sanitizeSourceText: (...args) => mockSanitize(...args),
16
+ }));
17
+ vi.mock("../../shared/html-utils.js", () => ({
18
+ decodeHtmlEntities: (...args) => mockDecodeEntities(...args),
19
+ }));
20
+ import { GoogleBooksSource, googleBooks } from "../../books/google-books.js";
21
+ // ============================================================================
22
+ // Helpers
23
+ // ============================================================================
24
+ /** Standard test subject. */
25
+ const subject = { id: 1, name: "John Wayne" };
26
+ /** Standard abort signal for tests. */
27
+ const signal = AbortSignal.timeout(5000);
28
+ /** Build a mock Google Books API response. */
29
+ function makeGoogleBooksResponse(items) {
30
+ return {
31
+ ok: true,
32
+ status: 200,
33
+ statusText: "OK",
34
+ json: async () => ({
35
+ totalItems: items.length,
36
+ items: items.length > 0 ? items : undefined,
37
+ }),
38
+ };
39
+ }
40
+ /** Description long enough to pass the 100-char minimum. */
41
+ const LONG_DESCRIPTION = "A".repeat(150);
42
+ // ============================================================================
43
+ // Lifecycle
44
+ // ============================================================================
45
+ beforeEach(() => {
46
+ mockSanitize.mockReset();
47
+ mockDecodeEntities.mockReset();
48
+ // Default: pass through
49
+ mockSanitize.mockImplementation((text) => text);
50
+ mockDecodeEntities.mockImplementation((text) => text);
51
+ vi.stubGlobal("fetch", vi.fn());
52
+ });
53
+ afterEach(() => {
54
+ vi.restoreAllMocks();
55
+ vi.unstubAllGlobals();
56
+ });
57
+ // ============================================================================
58
+ // Tests
59
+ // ============================================================================
60
+ describe("GoogleBooksSource", () => {
61
+ describe("metadata", () => {
62
+ it("has correct name, type, tier, domain, isFree, and cost", () => {
63
+ const source = new GoogleBooksSource({ apiKey: "test-key" });
64
+ expect(source.name).toBe("Google Books");
65
+ expect(source.type).toBe("google-books");
66
+ expect(source.reliabilityTier).toBe(ReliabilityTier.SECONDARY_COMPILATION);
67
+ expect(source.domain).toBe("www.googleapis.com");
68
+ expect(source.isFree).toBe(true);
69
+ expect(source.estimatedCostPerQuery).toBe(0);
70
+ });
71
+ });
72
+ // ==========================================================================
73
+ // isAvailable
74
+ // ==========================================================================
75
+ describe("isAvailable", () => {
76
+ it("returns true when apiKey is provided", () => {
77
+ const source = new GoogleBooksSource({ apiKey: "test-key" });
78
+ expect(source.isAvailable()).toBe(true);
79
+ });
80
+ it("returns false when no apiKey is provided and env var is unset", () => {
81
+ const original = process.env.GOOGLE_BOOKS_API_KEY;
82
+ delete process.env.GOOGLE_BOOKS_API_KEY;
83
+ const source = new GoogleBooksSource();
84
+ expect(source.isAvailable()).toBe(false);
85
+ if (original !== undefined) {
86
+ process.env.GOOGLE_BOOKS_API_KEY = original;
87
+ }
88
+ });
89
+ });
90
+ // ==========================================================================
91
+ // API call format
92
+ // ==========================================================================
93
+ describe("API call format", () => {
94
+ it("builds correct URL with all expected query params", async () => {
95
+ const mockFetch = vi.fn().mockResolvedValue(makeGoogleBooksResponse([]));
96
+ vi.stubGlobal("fetch", mockFetch);
97
+ const source = new GoogleBooksSource({ apiKey: "my-api-key" });
98
+ await source.lookup(subject, signal);
99
+ expect(mockFetch).toHaveBeenCalledTimes(1);
100
+ const [callUrl, callOptions] = mockFetch.mock.calls[0];
101
+ const parsed = new URL(callUrl);
102
+ expect(parsed.origin + parsed.pathname).toBe("https://www.googleapis.com/books/v1/volumes");
103
+ expect(parsed.searchParams.get("key")).toBe("my-api-key");
104
+ expect(parsed.searchParams.get("q")).toBe('"John Wayne" biography');
105
+ expect(parsed.searchParams.get("maxResults")).toBe("5");
106
+ expect(callOptions.signal).toBeDefined();
107
+ });
108
+ });
109
+ // ==========================================================================
110
+ // Response parsing
111
+ // ==========================================================================
112
+ describe("response parsing", () => {
113
+ it("returns finding with metadata from first item with enough text", async () => {
114
+ const mockFetch = vi.fn().mockResolvedValue(makeGoogleBooksResponse([
115
+ {
116
+ volumeInfo: {
117
+ title: "John Wayne: The Life",
118
+ description: LONG_DESCRIPTION,
119
+ authors: ["Author One"],
120
+ publishedDate: "2020-01-01",
121
+ infoLink: "https://books.google.com/books?id=abc123",
122
+ },
123
+ searchInfo: {
124
+ textSnippet: "A <b>bold</b> snippet about John Wayne&apos;s life.",
125
+ },
126
+ },
127
+ ]));
128
+ vi.stubGlobal("fetch", mockFetch);
129
+ const source = new GoogleBooksSource({ apiKey: "test-key" });
130
+ const result = await source.lookup(subject, signal);
131
+ expect(result).not.toBeNull();
132
+ expect(result.publication).toBe("Google Books");
133
+ expect(result.confidence).toBe(-1);
134
+ expect(result.costUsd).toBe(0);
135
+ expect(result.url).toBe("https://books.google.com/books?id=abc123");
136
+ expect(result.metadata?.title).toBe("John Wayne: The Life");
137
+ expect(result.metadata?.authors).toEqual(["Author One"]);
138
+ expect(result.metadata?.publishedDate).toBe("2020-01-01");
139
+ });
140
+ it("strips HTML tags from textSnippet and decodes entities", async () => {
141
+ const snippet = "A <b>bold</b> snippet &amp; more";
142
+ const decoded = "A bold snippet & more";
143
+ mockDecodeEntities.mockReturnValue(decoded);
144
+ const mockFetch = vi.fn().mockResolvedValue(makeGoogleBooksResponse([
145
+ {
146
+ volumeInfo: {
147
+ title: "Test Book",
148
+ description: LONG_DESCRIPTION,
149
+ infoLink: "https://books.google.com/books?id=xyz",
150
+ },
151
+ searchInfo: { textSnippet: snippet },
152
+ },
153
+ ]));
154
+ vi.stubGlobal("fetch", mockFetch);
155
+ const source = new GoogleBooksSource({ apiKey: "test-key" });
156
+ await source.lookup(subject, signal);
157
+ // decodeHtmlEntities should have been called with tag-stripped snippet
158
+ expect(mockDecodeEntities).toHaveBeenCalledWith("A bold snippet &amp; more");
159
+ // sanitizeSourceText should have been called with combined text
160
+ expect(mockSanitize).toHaveBeenCalled();
161
+ });
162
+ it("skips items with combined text shorter than 100 chars", async () => {
163
+ const mockFetch = vi.fn().mockResolvedValue(makeGoogleBooksResponse([
164
+ {
165
+ volumeInfo: {
166
+ title: "Short Book",
167
+ description: "Too short",
168
+ infoLink: "https://books.google.com/books?id=short",
169
+ },
170
+ },
171
+ {
172
+ volumeInfo: {
173
+ title: "Long Book",
174
+ description: LONG_DESCRIPTION,
175
+ infoLink: "https://books.google.com/books?id=long",
176
+ },
177
+ },
178
+ ]));
179
+ vi.stubGlobal("fetch", mockFetch);
180
+ const source = new GoogleBooksSource({ apiKey: "test-key" });
181
+ const result = await source.lookup(subject, signal);
182
+ expect(result).not.toBeNull();
183
+ expect(result.metadata?.title).toBe("Long Book");
184
+ });
185
+ });
186
+ // ==========================================================================
187
+ // Null returns
188
+ // ==========================================================================
189
+ describe("null returns", () => {
190
+ it("returns null when no items in response", async () => {
191
+ const mockFetch = vi.fn().mockResolvedValue(makeGoogleBooksResponse([]));
192
+ vi.stubGlobal("fetch", mockFetch);
193
+ const source = new GoogleBooksSource({ apiKey: "test-key" });
194
+ const result = await source.lookup(subject, signal);
195
+ expect(result).toBeNull();
196
+ });
197
+ it("returns null on API error (caught by base class)", async () => {
198
+ const mockFetch = vi.fn().mockResolvedValue({
199
+ ok: false,
200
+ status: 403,
201
+ statusText: "Forbidden",
202
+ json: async () => ({}),
203
+ });
204
+ vi.stubGlobal("fetch", mockFetch);
205
+ const source = new GoogleBooksSource({ apiKey: "test-key" });
206
+ const result = await source.lookup(subject, signal);
207
+ expect(result).toBeNull();
208
+ });
209
+ });
210
+ // ==========================================================================
211
+ // Factory function
212
+ // ==========================================================================
213
+ describe("factory", () => {
214
+ it("returns a GoogleBooksSource instance", () => {
215
+ const source = googleBooks({ apiKey: "test-key" });
216
+ expect(source).toBeInstanceOf(GoogleBooksSource);
217
+ expect(source.name).toBe("Google Books");
218
+ });
219
+ });
220
+ });
221
+ //# sourceMappingURL=google-books.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google-books.test.js","sourceRoot":"","sources":["../../../src/__tests__/books/google-books.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjD,+EAA+E;AAC/E,kEAAkE;AAClE,+EAA+E;AAE/E,MAAM,YAAY,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;AAC5B,MAAM,kBAAkB,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;AAElC,EAAE,CAAC,IAAI,CAAC,+BAA+B,EAAE,GAAG,EAAE,CAAC,CAAC;IAC9C,kBAAkB,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC;CAClE,CAAC,CAAC,CAAA;AAEH,EAAE,CAAC,IAAI,CAAC,4BAA4B,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3C,kBAAkB,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,kBAAkB,CAAC,GAAG,IAAI,CAAC;CACxE,CAAC,CAAC,CAAA;AAEH,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AAE5E,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E,6BAA6B;AAC7B,MAAM,OAAO,GAAG,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,CAAA;AAE7C,uCAAuC;AACvC,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;AAExC,8CAA8C;AAC9C,SAAS,uBAAuB,CAC9B,KASE;IAEF,OAAO;QACL,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,GAAG;QACX,UAAU,EAAE,IAAI;QAChB,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;YACjB,UAAU,EAAE,KAAK,CAAC,MAAM;YACxB,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;SAC5C,CAAC;KACH,CAAA;AACH,CAAC;AAED,4DAA4D;AAC5D,MAAM,gBAAgB,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;AAExC,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E,UAAU,CAAC,GAAG,EAAE;IACd,YAAY,CAAC,SAAS,EAAE,CAAA;IACxB,kBAAkB,CAAC,SAAS,EAAE,CAAA;IAC9B,wBAAwB;IACxB,YAAY,CAAC,kBAAkB,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,CAAA;IACvD,kBAAkB,CAAC,kBAAkB,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,CAAA;IAC7D,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;AACjC,CAAC,CAAC,CAAA;AAEF,SAAS,CAAC,GAAG,EAAE;IACb,EAAE,CAAC,eAAe,EAAE,CAAA;IACpB,EAAE,CAAC,gBAAgB,EAAE,CAAA;AACvB,CAAC,CAAC,CAAA;AAEF,+EAA+E;AAC/E,QAAQ;AACR,+EAA+E;AAE/E,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;QACxB,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;YAChE,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;YAE5D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;YACxC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;YACxC,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,qBAAqB,CAAC,CAAA;YAC1E,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAA;YAChD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAChC,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC9C,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,6EAA6E;IAC7E,cAAc;IACd,6EAA6E;IAE7E,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;QAC3B,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC9C,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;YAC5D,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACzC,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;YACvE,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAA;YACjD,OAAO,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAA;YAEvC,MAAM,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAA;YACtC,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAExC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,OAAO,CAAC,GAAG,CAAC,oBAAoB,GAAG,QAAQ,CAAA;YAC7C,CAAC;QACH,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,6EAA6E;IAC7E,kBAAkB;IAClB,6EAA6E;IAE7E,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;QAC/B,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;YACjE,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,uBAAuB,CAAC,EAAE,CAAC,CAAC,CAAA;YACxE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;YAEjC,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAA;YAC9D,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;YAEpC,MAAM,CAAC,SAAS,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;YAE1C,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAsC,CAAA;YAC3F,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAA;YAE/B,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAA;YAC3F,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;YACzD,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAA;YACnE,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YACvD,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAA;QAC1C,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,6EAA6E;IAC7E,mBAAmB;IACnB,6EAA6E;IAE7E,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;YAC9E,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CACzC,uBAAuB,CAAC;gBACtB;oBACE,UAAU,EAAE;wBACV,KAAK,EAAE,sBAAsB;wBAC7B,WAAW,EAAE,gBAAgB;wBAC7B,OAAO,EAAE,CAAC,YAAY,CAAC;wBACvB,aAAa,EAAE,YAAY;wBAC3B,QAAQ,EAAE,0CAA0C;qBACrD;oBACD,UAAU,EAAE;wBACV,WAAW,EAAE,qDAAqD;qBACnE;iBACF;aACF,CAAC,CACH,CAAA;YACD,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;YAEjC,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;YAC5D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;YAEnD,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAA;YAC7B,MAAM,CAAC,MAAO,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;YAChD,MAAM,CAAC,MAAO,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;YACnC,MAAM,CAAC,MAAO,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAC/B,MAAM,CAAC,MAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAA;YACpE,MAAM,CAAC,MAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAA;YAC5D,MAAM,CAAC,MAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAA;YACzD,MAAM,CAAC,MAAO,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QAC5D,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;YACtE,MAAM,OAAO,GAAG,kCAAkC,CAAA;YAClD,MAAM,OAAO,GAAG,uBAAuB,CAAA;YAEvC,kBAAkB,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;YAE3C,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CACzC,uBAAuB,CAAC;gBACtB;oBACE,UAAU,EAAE;wBACV,KAAK,EAAE,WAAW;wBAClB,WAAW,EAAE,gBAAgB;wBAC7B,QAAQ,EAAE,uCAAuC;qBAClD;oBACD,UAAU,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE;iBACrC;aACF,CAAC,CACH,CAAA;YACD,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;YAEjC,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;YAC5D,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;YAEpC,uEAAuE;YACvE,MAAM,CAAC,kBAAkB,CAAC,CAAC,oBAAoB,CAAC,2BAA2B,CAAC,CAAA;YAC5E,gEAAgE;YAChE,MAAM,CAAC,YAAY,CAAC,CAAC,gBAAgB,EAAE,CAAA;QACzC,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,uDAAuD,EAAE,KAAK,IAAI,EAAE;YACrE,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CACzC,uBAAuB,CAAC;gBACtB;oBACE,UAAU,EAAE;wBACV,KAAK,EAAE,YAAY;wBACnB,WAAW,EAAE,WAAW;wBACxB,QAAQ,EAAE,yCAAyC;qBACpD;iBACF;gBACD;oBACE,UAAU,EAAE;wBACV,KAAK,EAAE,WAAW;wBAClB,WAAW,EAAE,gBAAgB;wBAC7B,QAAQ,EAAE,wCAAwC;qBACnD;iBACF;aACF,CAAC,CACH,CAAA;YACD,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;YAEjC,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;YAC5D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;YAEnD,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAA;YAC7B,MAAM,CAAC,MAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QACnD,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,6EAA6E;IAC7E,eAAe;IACf,6EAA6E;IAE7E,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;QAC5B,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,uBAAuB,CAAC,EAAE,CAAC,CAAC,CAAA;YACxE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;YAEjC,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;YAC5D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;YAEnD,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAA;QAC3B,CAAC,CAAC,CAAA;QAEF,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;YAChE,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC;gBAC1C,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,GAAG;gBACX,UAAU,EAAE,WAAW;gBACvB,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aACvB,CAAC,CAAA;YACF,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;YAEjC,MAAM,MAAM,GAAG,IAAI,iBAAiB,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;YAC5D,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;YAEnD,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAA;QAC3B,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,6EAA6E;IAC7E,mBAAmB;IACnB,6EAA6E;IAE7E,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE;QACvB,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;YAC9C,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAA;YAClD,MAAM,CAAC,MAAM,CAAC,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAA;YAChD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;QAC1C,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Tests for OpenLibrarySource.
3
+ *
4
+ * Mocks the global fetch API. Open Library requires no API key
5
+ * and returns structured JSON — no HTML parsing needed.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=open-library.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-library.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/books/open-library.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Tests for OpenLibrarySource.
3
+ *
4
+ * Mocks the global fetch API. Open Library requires no API key
5
+ * and returns structured JSON — no HTML parsing needed.
6
+ */
7
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
8
+ import { ReliabilityTier } from "@debriefer/core";
9
+ import { OpenLibrarySource, openLibrary } from "../../books/open-library.js";
10
+ // ============================================================================
11
+ // Helpers
12
+ // ============================================================================
13
+ /** Standard test subject. */
14
+ const subject = { id: 1, name: "John Wayne" };
15
+ /** Standard abort signal for tests. */
16
+ const signal = AbortSignal.timeout(5000);
17
+ /** Build a mock Open Library search response. */
18
+ function makeOpenLibraryResponse(docs) {
19
+ return {
20
+ ok: true,
21
+ status: 200,
22
+ statusText: "OK",
23
+ json: async () => ({
24
+ numFound: docs.length,
25
+ docs,
26
+ }),
27
+ };
28
+ }
29
+ // ============================================================================
30
+ // Lifecycle
31
+ // ============================================================================
32
+ beforeEach(() => {
33
+ vi.stubGlobal("fetch", vi.fn());
34
+ });
35
+ afterEach(() => {
36
+ vi.restoreAllMocks();
37
+ vi.unstubAllGlobals();
38
+ });
39
+ // ============================================================================
40
+ // Tests
41
+ // ============================================================================
42
+ describe("OpenLibrarySource", () => {
43
+ describe("metadata", () => {
44
+ it("has correct name, type, tier, domain, isFree, and cost", () => {
45
+ const source = new OpenLibrarySource();
46
+ expect(source.name).toBe("Open Library");
47
+ expect(source.type).toBe("open-library");
48
+ expect(source.reliabilityTier).toBe(ReliabilityTier.SECONDARY_COMPILATION);
49
+ expect(source.domain).toBe("openlibrary.org");
50
+ expect(source.isFree).toBe(true);
51
+ expect(source.estimatedCostPerQuery).toBe(0);
52
+ });
53
+ });
54
+ // ==========================================================================
55
+ // isAvailable
56
+ // ==========================================================================
57
+ describe("isAvailable", () => {
58
+ it("returns true always (no API key required)", () => {
59
+ const source = new OpenLibrarySource();
60
+ expect(source.isAvailable()).toBe(true);
61
+ });
62
+ });
63
+ // ==========================================================================
64
+ // API call format
65
+ // ==========================================================================
66
+ describe("API call format", () => {
67
+ it("builds correct URL with expected query params", async () => {
68
+ const mockFetch = vi.fn().mockResolvedValue(makeOpenLibraryResponse([]));
69
+ vi.stubGlobal("fetch", mockFetch);
70
+ const source = new OpenLibrarySource();
71
+ await source.lookup(subject, signal);
72
+ expect(mockFetch).toHaveBeenCalledTimes(1);
73
+ const [callUrl, callOptions] = mockFetch.mock.calls[0];
74
+ const parsed = new URL(callUrl);
75
+ expect(parsed.origin + parsed.pathname).toBe("https://openlibrary.org/search.json");
76
+ expect(parsed.searchParams.get("q")).toBe('"John Wayne" biography');
77
+ expect(parsed.searchParams.get("limit")).toBe("5");
78
+ expect(callOptions.signal).toBeDefined();
79
+ });
80
+ });
81
+ // ==========================================================================
82
+ // Response parsing
83
+ // ==========================================================================
84
+ describe("response parsing", () => {
85
+ it("returns finding with metadata from first doc with a title", async () => {
86
+ const mockFetch = vi.fn().mockResolvedValue(makeOpenLibraryResponse([
87
+ {
88
+ key: "/works/OL12345W",
89
+ title: "John Wayne: American Legend",
90
+ author_name: ["Jane Smith"],
91
+ first_publish_year: 1998,
92
+ ia: ["johnwaynelegend00smit"],
93
+ },
94
+ ]));
95
+ vi.stubGlobal("fetch", mockFetch);
96
+ const source = new OpenLibrarySource();
97
+ const result = await source.lookup(subject, signal);
98
+ expect(result).not.toBeNull();
99
+ expect(result.text).toBe("John Wayne: American Legend by Jane Smith (1998)");
100
+ expect(result.publication).toBe("Open Library");
101
+ expect(result.confidence).toBe(-1);
102
+ expect(result.costUsd).toBe(0);
103
+ expect(result.url).toBe("https://openlibrary.org/works/OL12345W");
104
+ expect(result.metadata?.title).toBe("John Wayne: American Legend");
105
+ expect(result.metadata?.authors).toEqual(["Jane Smith"]);
106
+ expect(result.metadata?.firstPublishYear).toBe(1998);
107
+ });
108
+ it("handles docs with multiple authors", async () => {
109
+ const mockFetch = vi.fn().mockResolvedValue(makeOpenLibraryResponse([
110
+ {
111
+ key: "/works/OL999W",
112
+ title: "Dual Authors Book",
113
+ author_name: ["Author A", "Author B"],
114
+ first_publish_year: 2005,
115
+ },
116
+ ]));
117
+ vi.stubGlobal("fetch", mockFetch);
118
+ const source = new OpenLibrarySource();
119
+ const result = await source.lookup(subject, signal);
120
+ expect(result).not.toBeNull();
121
+ expect(result.text).toBe("Dual Authors Book by Author A, Author B (2005)");
122
+ });
123
+ });
124
+ // ==========================================================================
125
+ // Null returns
126
+ // ==========================================================================
127
+ describe("null returns", () => {
128
+ it("returns null when no docs in response", async () => {
129
+ const mockFetch = vi.fn().mockResolvedValue(makeOpenLibraryResponse([]));
130
+ vi.stubGlobal("fetch", mockFetch);
131
+ const source = new OpenLibrarySource();
132
+ const result = await source.lookup(subject, signal);
133
+ expect(result).toBeNull();
134
+ });
135
+ it("returns null on API error (caught by base class)", async () => {
136
+ const mockFetch = vi.fn().mockResolvedValue({
137
+ ok: false,
138
+ status: 500,
139
+ statusText: "Internal Server Error",
140
+ json: async () => ({}),
141
+ });
142
+ vi.stubGlobal("fetch", mockFetch);
143
+ const source = new OpenLibrarySource();
144
+ const result = await source.lookup(subject, signal);
145
+ expect(result).toBeNull();
146
+ });
147
+ });
148
+ // ==========================================================================
149
+ // Factory function
150
+ // ==========================================================================
151
+ describe("factory", () => {
152
+ it("returns an OpenLibrarySource instance", () => {
153
+ const source = openLibrary();
154
+ expect(source).toBeInstanceOf(OpenLibrarySource);
155
+ expect(source.name).toBe("Open Library");
156
+ });
157
+ });
158
+ });
159
+ //# sourceMappingURL=open-library.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-library.test.js","sourceRoot":"","sources":["../../../src/__tests__/books/open-library.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Tests for GuardianSource.
3
+ *
4
+ * Mocks the global fetch API. Does NOT mock pipeline utilities
5
+ * (Guardian uses its own API, not fetchPage/extractArticleContent).
6
+ * sanitizeSourceText is mocked to isolate Guardian-specific logic.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=guardian.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardian.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/news/guardian.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Tests for GuardianSource.
3
+ *
4
+ * Mocks the global fetch API. Does NOT mock pipeline utilities
5
+ * (Guardian uses its own API, not fetchPage/extractArticleContent).
6
+ * sanitizeSourceText is mocked to isolate Guardian-specific logic.
7
+ */
8
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
9
+ import { ReliabilityTier } from "@debriefer/core";
10
+ // ============================================================================
11
+ // Mocks — must be declared before importing the module under test
12
+ // ============================================================================
13
+ const mockSanitize = vi.fn();
14
+ vi.mock("../../shared/sanitize-text.js", () => ({
15
+ sanitizeSourceText: (...args) => mockSanitize(...args),
16
+ }));
17
+ import { GuardianSource, guardian } from "../../news/guardian.js";
18
+ // ============================================================================
19
+ // Helpers
20
+ // ============================================================================
21
+ /** Standard test subject. */
22
+ const subject = { id: 1, name: "John Wayne" };
23
+ /** Standard abort signal for tests. */
24
+ const signal = AbortSignal.timeout(5000);
25
+ /** Build a mock Guardian API response. */
26
+ function makeGuardianResponse(results) {
27
+ return {
28
+ ok: true,
29
+ status: 200,
30
+ statusText: "OK",
31
+ json: async () => ({
32
+ response: {
33
+ status: "ok",
34
+ results,
35
+ },
36
+ }),
37
+ };
38
+ }
39
+ /** Body text long enough to pass the 200-char minimum. */
40
+ const LONG_TEXT = "A".repeat(300);
41
+ // ============================================================================
42
+ // Lifecycle
43
+ // ============================================================================
44
+ beforeEach(() => {
45
+ mockSanitize.mockReset();
46
+ vi.stubGlobal("fetch", vi.fn());
47
+ });
48
+ afterEach(() => {
49
+ vi.restoreAllMocks();
50
+ vi.unstubAllGlobals();
51
+ });
52
+ // ============================================================================
53
+ // Metadata
54
+ // ============================================================================
55
+ describe("GuardianSource", () => {
56
+ describe("metadata", () => {
57
+ it("has correct name, type, tier, domain, isFree, and cost", () => {
58
+ const source = new GuardianSource({ apiKey: "test-key" });
59
+ expect(source.name).toBe("The Guardian");
60
+ expect(source.type).toBe("guardian");
61
+ expect(source.reliabilityTier).toBe(ReliabilityTier.TIER_1_NEWS);
62
+ expect(source.domain).toBe("content.guardianapis.com");
63
+ expect(source.isFree).toBe(true);
64
+ expect(source.estimatedCostPerQuery).toBe(0);
65
+ });
66
+ });
67
+ // ==========================================================================
68
+ // isAvailable
69
+ // ==========================================================================
70
+ describe("isAvailable", () => {
71
+ it("returns true when apiKey is provided", () => {
72
+ const source = new GuardianSource({ apiKey: "test-key" });
73
+ expect(source.isAvailable()).toBe(true);
74
+ });
75
+ it("returns false when no apiKey is provided and env var is unset", () => {
76
+ const original = process.env.GUARDIAN_API_KEY;
77
+ delete process.env.GUARDIAN_API_KEY;
78
+ const source = new GuardianSource();
79
+ expect(source.isAvailable()).toBe(false);
80
+ // Restore
81
+ if (original !== undefined) {
82
+ process.env.GUARDIAN_API_KEY = original;
83
+ }
84
+ });
85
+ });
86
+ // ==========================================================================
87
+ // API call format
88
+ // ==========================================================================
89
+ describe("API call format", () => {
90
+ it("builds correct URL with all expected query params", async () => {
91
+ const mockFetch = vi.fn().mockResolvedValue(makeGuardianResponse([]));
92
+ vi.stubGlobal("fetch", mockFetch);
93
+ const source = new GuardianSource({ apiKey: "my-api-key" });
94
+ await source.lookup(subject, signal);
95
+ expect(mockFetch).toHaveBeenCalledTimes(1);
96
+ const [callUrl, callOptions] = mockFetch.mock.calls[0];
97
+ const parsed = new URL(callUrl);
98
+ expect(parsed.origin + parsed.pathname).toBe("https://content.guardianapis.com/search");
99
+ expect(parsed.searchParams.get("api-key")).toBe("my-api-key");
100
+ expect(parsed.searchParams.get("q")).toContain('"John Wayne"');
101
+ expect(parsed.searchParams.get("show-fields")).toBe("bodyText,standfirst,trailText");
102
+ expect(parsed.searchParams.get("page-size")).toBe("10");
103
+ expect(parsed.searchParams.get("order-by")).toBe("relevance");
104
+ expect(callOptions.signal).toBeDefined();
105
+ });
106
+ });
107
+ // ==========================================================================
108
+ // Response parsing — article selection
109
+ // ==========================================================================
110
+ describe("article selection", () => {
111
+ it("picks article with bio keyword in title", async () => {
112
+ const mockFetch = vi.fn().mockResolvedValue(makeGuardianResponse([
113
+ {
114
+ webTitle: "John Wayne filmography roundup",
115
+ webUrl: "https://theguardian.com/film/roundup",
116
+ fields: { bodyText: LONG_TEXT },
117
+ },
118
+ {
119
+ webTitle: "John Wayne profile: the man behind the legend",
120
+ webUrl: "https://theguardian.com/film/profile",
121
+ fields: { bodyText: LONG_TEXT },
122
+ },
123
+ ]));
124
+ vi.stubGlobal("fetch", mockFetch);
125
+ mockSanitize.mockReturnValue(LONG_TEXT);
126
+ const source = new GuardianSource({ apiKey: "test-key" });
127
+ const result = await source.lookup(subject, signal);
128
+ expect(result).not.toBeNull();
129
+ expect(result.url).toBe("https://theguardian.com/film/profile");
130
+ expect(result.metadata?.title).toBe("John Wayne profile: the man behind the legend");
131
+ });
132
+ it("falls back to first result when no keyword match", async () => {
133
+ const mockFetch = vi.fn().mockResolvedValue(makeGuardianResponse([
134
+ {
135
+ webTitle: "John Wayne in new film role",
136
+ webUrl: "https://theguardian.com/film/new-role",
137
+ fields: { bodyText: LONG_TEXT },
138
+ },
139
+ {
140
+ webTitle: "John Wayne at the Oscars",
141
+ webUrl: "https://theguardian.com/film/oscars",
142
+ fields: { bodyText: LONG_TEXT },
143
+ },
144
+ ]));
145
+ vi.stubGlobal("fetch", mockFetch);
146
+ mockSanitize.mockReturnValue(LONG_TEXT);
147
+ const source = new GuardianSource({ apiKey: "test-key" });
148
+ const result = await source.lookup(subject, signal);
149
+ expect(result).not.toBeNull();
150
+ expect(result.url).toBe("https://theguardian.com/film/new-role");
151
+ });
152
+ });
153
+ // ==========================================================================
154
+ // Response parsing — body text
155
+ // ==========================================================================
156
+ describe("body text extraction", () => {
157
+ it("returns bodyText as source text", async () => {
158
+ const bodyText = "B".repeat(300);
159
+ const mockFetch = vi.fn().mockResolvedValue(makeGuardianResponse([
160
+ {
161
+ webTitle: "John Wayne profile",
162
+ webUrl: "https://theguardian.com/film/profile",
163
+ fields: { bodyText },
164
+ },
165
+ ]));
166
+ vi.stubGlobal("fetch", mockFetch);
167
+ mockSanitize.mockReturnValue(bodyText);
168
+ const source = new GuardianSource({ apiKey: "test-key" });
169
+ const result = await source.lookup(subject, signal);
170
+ expect(result).not.toBeNull();
171
+ expect(result.text).toBe(bodyText);
172
+ expect(result.confidence).toBe(-1);
173
+ expect(result.costUsd).toBe(0);
174
+ expect(result.publication).toBe("The Guardian");
175
+ expect(mockSanitize).toHaveBeenCalledWith(bodyText);
176
+ });
177
+ });
178
+ // ==========================================================================
179
+ // Null returns
180
+ // ==========================================================================
181
+ describe("null returns", () => {
182
+ it("returns null when no results", async () => {
183
+ const mockFetch = vi.fn().mockResolvedValue(makeGuardianResponse([]));
184
+ vi.stubGlobal("fetch", mockFetch);
185
+ const source = new GuardianSource({ apiKey: "test-key" });
186
+ const result = await source.lookup(subject, signal);
187
+ expect(result).toBeNull();
188
+ });
189
+ it("returns null on API error (caught by base class)", async () => {
190
+ const mockFetch = vi.fn().mockResolvedValue({
191
+ ok: false,
192
+ status: 403,
193
+ statusText: "Forbidden",
194
+ json: async () => ({}),
195
+ });
196
+ vi.stubGlobal("fetch", mockFetch);
197
+ const source = new GuardianSource({ apiKey: "test-key" });
198
+ // BaseResearchSource.lookup() catches errors from fetchResult and returns null
199
+ const result = await source.lookup(subject, signal);
200
+ expect(result).toBeNull();
201
+ });
202
+ it("returns null when apiKey is missing", async () => {
203
+ const original = process.env.GUARDIAN_API_KEY;
204
+ delete process.env.GUARDIAN_API_KEY;
205
+ const source = new GuardianSource();
206
+ const result = await source.lookup(subject, signal);
207
+ expect(result).toBeNull();
208
+ if (original !== undefined) {
209
+ process.env.GUARDIAN_API_KEY = original;
210
+ }
211
+ });
212
+ });
213
+ // ==========================================================================
214
+ // Factory function
215
+ // ==========================================================================
216
+ describe("factory", () => {
217
+ it("returns a GuardianSource instance", () => {
218
+ const source = guardian({ apiKey: "test-key" });
219
+ expect(source).toBeInstanceOf(GuardianSource);
220
+ expect(source.name).toBe("The Guardian");
221
+ });
222
+ });
223
+ });
224
+ //# sourceMappingURL=guardian.test.js.map