@zonuexe/techbook-mcp 0.2.3 → 0.2.4

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 (135) hide show
  1. package/CHANGELOG.md +15 -1
  2. package/README.md +39 -20
  3. package/dist/adapters/publishers/google-books.d.ts +4 -0
  4. package/dist/adapters/publishers/google-books.d.ts.map +1 -0
  5. package/dist/adapters/publishers/google-books.js +75 -0
  6. package/dist/adapters/publishers/google-books.js.map +1 -0
  7. package/dist/adapters/publishers/isbn-publisher-codes.d.ts +21 -0
  8. package/dist/adapters/publishers/isbn-publisher-codes.d.ts.map +1 -0
  9. package/dist/adapters/publishers/isbn-publisher-codes.js +49 -0
  10. package/dist/adapters/publishers/isbn-publisher-codes.js.map +1 -0
  11. package/dist/adapters/publishers/juse-p.d.ts +3 -0
  12. package/dist/adapters/publishers/juse-p.d.ts.map +1 -0
  13. package/dist/adapters/publishers/juse-p.js +110 -0
  14. package/dist/adapters/publishers/juse-p.js.map +1 -0
  15. package/dist/adapters/publishers/registry.d.ts.map +1 -1
  16. package/dist/adapters/publishers/registry.js +4 -0
  17. package/dist/adapters/publishers/registry.js.map +1 -1
  18. package/dist/application/get-book-by-isbn.d.ts +3 -2
  19. package/dist/application/get-book-by-isbn.d.ts.map +1 -1
  20. package/dist/application/get-book-by-isbn.js +22 -3
  21. package/dist/application/get-book-by-isbn.js.map +1 -1
  22. package/dist/config/credentials.d.ts +8 -0
  23. package/dist/config/credentials.d.ts.map +1 -0
  24. package/dist/config/credentials.js +32 -0
  25. package/dist/config/credentials.js.map +1 -0
  26. package/dist/main.js +15 -1
  27. package/dist/main.js.map +1 -1
  28. package/dist/setup.d.ts +2 -0
  29. package/dist/setup.d.ts.map +1 -0
  30. package/dist/setup.js +43 -0
  31. package/dist/setup.js.map +1 -0
  32. package/flake.lock +61 -0
  33. package/package.json +1 -1
  34. package/.claude/settings.local.json +0 -38
  35. package/.codex/skills/techbook-mcp-release-prep/SKILL.md +0 -105
  36. package/.github/workflows/test.yml +0 -72
  37. package/.oxlintrc.json +0 -12
  38. package/AGENTS.md +0 -100
  39. package/deno.json +0 -3
  40. package/src/adapters/cache/memory-cache.ts +0 -31
  41. package/src/adapters/cache/null-cache.ts +0 -8
  42. package/src/adapters/calil.ts +0 -57
  43. package/src/adapters/html/cheerio-parser.ts +0 -50
  44. package/src/adapters/http/fetch-client.ts +0 -47
  45. package/src/adapters/http/mock-client.ts +0 -77
  46. package/src/adapters/openbd.ts +0 -142
  47. package/src/adapters/publishers/base.ts +0 -279
  48. package/src/adapters/publishers/book-tech.ts +0 -117
  49. package/src/adapters/publishers/born-digital.ts +0 -143
  50. package/src/adapters/publishers/coronasha.ts +0 -139
  51. package/src/adapters/publishers/gihyo.ts +0 -120
  52. package/src/adapters/publishers/impress.ts +0 -103
  53. package/src/adapters/publishers/lambdanote.ts +0 -146
  54. package/src/adapters/publishers/manatee.ts +0 -113
  55. package/src/adapters/publishers/maruzen-publishing.ts +0 -129
  56. package/src/adapters/publishers/optronics.ts +0 -113
  57. package/src/adapters/publishers/oreilly-japan.ts +0 -133
  58. package/src/adapters/publishers/peaks.ts +0 -98
  59. package/src/adapters/publishers/personal-media.ts +0 -168
  60. package/src/adapters/publishers/registry.ts +0 -38
  61. package/src/adapters/publishers/rutles.ts +0 -149
  62. package/src/adapters/publishers/saiensu.ts +0 -136
  63. package/src/adapters/publishers/seshop.ts +0 -121
  64. package/src/adapters/publishers/tatsu-zine.ts +0 -142
  65. package/src/adapters/publishers/techbookfest.ts +0 -179
  66. package/src/application/get-book-by-isbn.ts +0 -50
  67. package/src/application/get-book-detail.ts +0 -40
  68. package/src/application/search-books.ts +0 -64
  69. package/src/domain/book.ts +0 -35
  70. package/src/domain/publisher.ts +0 -18
  71. package/src/main.ts +0 -14
  72. package/src/mcp/server.ts +0 -113
  73. package/src/mcp/tools.ts +0 -71
  74. package/src/ports/cache.ts +0 -5
  75. package/src/ports/html-parser.ts +0 -15
  76. package/src/ports/http.ts +0 -17
  77. package/tests/fixtures/book-tech-detail.html +0 -51
  78. package/tests/fixtures/book-tech-search.html +0 -91
  79. package/tests/fixtures/born-digital-detail.html +0 -62
  80. package/tests/fixtures/born-digital-search.html +0 -51
  81. package/tests/fixtures/calil-book.html +0 -987
  82. package/tests/fixtures/coronasha-detail.html +0 -41
  83. package/tests/fixtures/coronasha-search.html +0 -61
  84. package/tests/fixtures/gihyo-detail.html +0 -42
  85. package/tests/fixtures/gihyo-search.json +0 -54
  86. package/tests/fixtures/impress-detail-epub.html +0 -746
  87. package/tests/fixtures/impress-detail-social.html +0 -689
  88. package/tests/fixtures/lambdanote-search.html +0 -66
  89. package/tests/fixtures/manatee-detail.html +0 -53
  90. package/tests/fixtures/manatee-search.html +0 -59
  91. package/tests/fixtures/maruzen-detail.html +0 -51
  92. package/tests/fixtures/maruzen-search.html +0 -60
  93. package/tests/fixtures/openbd-response.json +0 -110
  94. package/tests/fixtures/optronics-detail.html +0 -30
  95. package/tests/fixtures/optronics-search.html +0 -75
  96. package/tests/fixtures/oreilly-detail.html +0 -52
  97. package/tests/fixtures/oreilly-ebook-list.html +0 -53
  98. package/tests/fixtures/peaks-detail.html +0 -39
  99. package/tests/fixtures/peaks-top.html +0 -50
  100. package/tests/fixtures/personal-media-detail.html +0 -32
  101. package/tests/fixtures/personal-media-search.html +0 -39
  102. package/tests/fixtures/rutles-detail.html +0 -32
  103. package/tests/fixtures/rutles-search.html +0 -62
  104. package/tests/fixtures/saiensu-detail.html +0 -41
  105. package/tests/fixtures/saiensu-search.html +0 -65
  106. package/tests/fixtures/seshop-detail.html +0 -45
  107. package/tests/fixtures/seshop-search.html +0 -58
  108. package/tests/fixtures/tatsu-zine-detail-free.html +0 -24
  109. package/tests/fixtures/tatsu-zine-search.html +0 -40
  110. package/tests/fixtures/techbookfest-search.json +0 -73
  111. package/tests/unit/adapters/base.test.ts +0 -441
  112. package/tests/unit/adapters/calil.test.ts +0 -69
  113. package/tests/unit/adapters/openbd.test.ts +0 -185
  114. package/tests/unit/adapters/publishers/book-tech.test.ts +0 -186
  115. package/tests/unit/adapters/publishers/born-digital.test.ts +0 -194
  116. package/tests/unit/adapters/publishers/coronasha.test.ts +0 -207
  117. package/tests/unit/adapters/publishers/gihyo.test.ts +0 -137
  118. package/tests/unit/adapters/publishers/impress.test.ts +0 -129
  119. package/tests/unit/adapters/publishers/lambdanote.test.ts +0 -85
  120. package/tests/unit/adapters/publishers/manatee.test.ts +0 -165
  121. package/tests/unit/adapters/publishers/maruzen-publishing.test.ts +0 -179
  122. package/tests/unit/adapters/publishers/optronics.test.ts +0 -208
  123. package/tests/unit/adapters/publishers/oreilly-japan.test.ts +0 -194
  124. package/tests/unit/adapters/publishers/peaks.test.ts +0 -177
  125. package/tests/unit/adapters/publishers/personal-media.test.ts +0 -199
  126. package/tests/unit/adapters/publishers/rutles.test.ts +0 -173
  127. package/tests/unit/adapters/publishers/saiensu.test.ts +0 -169
  128. package/tests/unit/adapters/publishers/seshop.test.ts +0 -174
  129. package/tests/unit/adapters/publishers/tatsu-zine.test.ts +0 -172
  130. package/tests/unit/adapters/publishers/techbookfest.test.ts +0 -94
  131. package/tests/unit/adapters/registry.test.ts +0 -37
  132. package/tests/unit/application/get-book-by-isbn.test.ts +0 -176
  133. package/tests/unit/application/get-book-detail.test.ts +0 -102
  134. package/tests/unit/application/search-books.test.ts +0 -137
  135. package/tsconfig.json +0 -17
@@ -1,186 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { readFile } from "node:fs/promises";
4
- import { join } from "node:path";
5
- import { bookTechAdapter } from "../../../../src/adapters/publishers/book-tech.js";
6
- import { MockHttpClient } from "../../../../src/adapters/http/mock-client.js";
7
- import { CheerioHtmlParser } from "../../../../src/adapters/html/cheerio-parser.js";
8
- import { NullCacheStore } from "../../../../src/adapters/cache/null-cache.js";
9
-
10
- const FIXTURES_DIR = join(import.meta.dirname, "../../../fixtures");
11
-
12
- function makeDeps(http: MockHttpClient) {
13
- return { http, parser: new CheerioHtmlParser(), cache: new NullCacheStore() };
14
- }
15
-
16
- async function loadFixture(name: string): Promise<string> {
17
- return readFile(join(FIXTURES_DIR, name), "utf-8");
18
- }
19
-
20
- describe("bookTechAdapter", () => {
21
- describe("search()", () => {
22
- it("BookRecord[] を返す", async () => {
23
- const body = await loadFixture("book-tech-search.html");
24
- const http = new MockHttpClient().addResponse(
25
- "https://book-tech.com/books",
26
- { status: 200, body },
27
- );
28
-
29
- const results = await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
30
-
31
- assert.strictEqual(results.length, 2);
32
- assert.partialDeepStrictEqual(results[0], {
33
- title: "次のステップへ!React実践開発 サクサク作って学ぶ UI/テスト/デプロイ",
34
- publisher: "インプレス NextPublishing",
35
- url: "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
36
- price: 2178,
37
- publishedAt: "2026-02-20",
38
- });
39
- });
40
-
41
- it("著者名から役割語を除去する", async () => {
42
- const body = await loadFixture("book-tech-search.html");
43
- const http = new MockHttpClient().addResponse(
44
- "https://book-tech.com/books",
45
- { status: 200, body },
46
- );
47
-
48
- const results = await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
49
-
50
- assert.deepStrictEqual(results[0].authors, ["philosophy"]);
51
- assert.deepStrictEqual(results[1].authors, ["井手 優太"]);
52
- });
53
-
54
- it("coverImageUrl が設定される", async () => {
55
- const body = await loadFixture("book-tech-search.html");
56
- const http = new MockHttpClient().addResponse(
57
- "https://book-tech.com/books",
58
- { status: 200, body },
59
- );
60
-
61
- const results = await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
62
-
63
- assert.strictEqual(
64
- results[0].coverImageUrl,
65
- "https://booktech-share.s3-ap-northeast-1.amazonaws.com/books/d80ffe3d.webp",
66
- );
67
- });
68
-
69
- it("ebookStores に BOOK TECH (social DRM) が含まれる", async () => {
70
- const body = await loadFixture("book-tech-search.html");
71
- const http = new MockHttpClient().addResponse(
72
- "https://book-tech.com/books",
73
- { status: 200, body },
74
- );
75
-
76
- const results = await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
77
-
78
- assert.deepStrictEqual(results[0].ebookStores, [
79
- {
80
- name: "BOOK TECH",
81
- url: "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
82
- drm: "social",
83
- },
84
- ]);
85
- });
86
-
87
- it("title も author も空の場合は [] を返しHTTPを呼ばない", async () => {
88
- const http = new MockHttpClient();
89
-
90
- const results = await bookTechAdapter.search({}, makeDeps(http));
91
-
92
- assert.deepStrictEqual(results, []);
93
- assert.strictEqual(http.calls.length, 0);
94
- });
95
-
96
- it("検索リクエストに q[...] パラメータが含まれる", async () => {
97
- const body = await loadFixture("book-tech-search.html");
98
- const http = new MockHttpClient().addResponse(
99
- "https://book-tech.com/books",
100
- { status: 200, body },
101
- );
102
-
103
- await bookTechAdapter.search({ title: "TypeScript" }, makeDeps(http));
104
-
105
- assert.ok(http.calls[0].includes("TypeScript"));
106
- assert.ok(http.calls[0].includes("title_or_overview"));
107
- });
108
- });
109
-
110
- describe("getDetail()", () => {
111
- it("詳細情報を返す", async () => {
112
- const body = await loadFixture("book-tech-detail.html");
113
- const http = new MockHttpClient().addResponse(
114
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
115
- { status: 200, body },
116
- );
117
-
118
- const book = await bookTechAdapter.getDetail(
119
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
120
- makeDeps(http),
121
- );
122
-
123
- assert.partialDeepStrictEqual(book, {
124
- title: "次のステップへ!React実践開発 サクサク作って学ぶ UI/テスト/デプロイ",
125
- publisher: "インプレス NextPublishing",
126
- isbn: "9784295604136",
127
- price: 2178,
128
- publishedAt: "2026-02-20",
129
- });
130
- });
131
-
132
- it("著者名から役割語を除去する", async () => {
133
- const body = await loadFixture("book-tech-detail.html");
134
- const http = new MockHttpClient().addResponse(
135
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
136
- { status: 200, body },
137
- );
138
-
139
- const book = await bookTechAdapter.getDetail(
140
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
141
- makeDeps(http),
142
- );
143
-
144
- assert.deepStrictEqual(book.authors, ["philosophy"]);
145
- });
146
-
147
- it("coverImageUrl が設定される", async () => {
148
- const body = await loadFixture("book-tech-detail.html");
149
- const http = new MockHttpClient().addResponse(
150
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
151
- { status: 200, body },
152
- );
153
-
154
- const book = await bookTechAdapter.getDetail(
155
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
156
- makeDeps(http),
157
- );
158
-
159
- assert.strictEqual(
160
- book.coverImageUrl,
161
- "https://booktech-share.s3-ap-northeast-1.amazonaws.com/books/d80ffe3d.webp",
162
- );
163
- });
164
-
165
- it("ebookStores に BOOK TECH (social DRM) が含まれる", async () => {
166
- const body = await loadFixture("book-tech-detail.html");
167
- const http = new MockHttpClient().addResponse(
168
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
169
- { status: 200, body },
170
- );
171
-
172
- const book = await bookTechAdapter.getDetail(
173
- "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
174
- makeDeps(http),
175
- );
176
-
177
- assert.deepStrictEqual(book.ebookStores, [
178
- {
179
- name: "BOOK TECH",
180
- url: "https://book-tech.com/books/d80ffe3d-f3fe-458b-95ee-b4dd3327fab2",
181
- drm: "social",
182
- },
183
- ]);
184
- });
185
- });
186
- });
@@ -1,194 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { readFile } from "node:fs/promises";
4
- import { join } from "node:path";
5
- import { bornDigitalAdapter } from "../../../../src/adapters/publishers/born-digital.js";
6
- import { MockHttpClient } from "../../../../src/adapters/http/mock-client.js";
7
- import { CheerioHtmlParser } from "../../../../src/adapters/html/cheerio-parser.js";
8
- import { NullCacheStore } from "../../../../src/adapters/cache/null-cache.js";
9
-
10
- const FIXTURES_DIR = join(import.meta.dirname, "../../../fixtures");
11
-
12
- function makeDeps(http: MockHttpClient) {
13
- return { http, parser: new CheerioHtmlParser(), cache: new NullCacheStore() };
14
- }
15
-
16
- async function loadFixture(name: string): Promise<string> {
17
- return readFile(join(FIXTURES_DIR, name), "utf-8");
18
- }
19
-
20
- describe("bornDigitalAdapter", () => {
21
- describe("search()", () => {
22
- it("電子書籍のみ BookRecord[] を返す(紙書籍は除外)", async () => {
23
- const body = await loadFixture("born-digital-search.html");
24
- const http = new MockHttpClient().addResponse(
25
- "https://wgn-obs.shop-pro.jp/",
26
- { status: 200, body },
27
- );
28
-
29
- const results = await bornDigitalAdapter.search({ title: "HTML" }, makeDeps(http));
30
-
31
- // フィクスチャには電子2件・紙1件あり、電子のみ返す
32
- assert.strictEqual(results.length, 2);
33
- assert.partialDeepStrictEqual(results[0], {
34
- title: "【PDFダウンロード版】HTML解体新書 ー仕様から紐解く本格入門",
35
- publisher: "ボーンデジタル",
36
- url: "https://wgn-obs.shop-pro.jp/?pid=167400957",
37
- price: 3520,
38
- });
39
- assert.partialDeepStrictEqual(results[1], {
40
- title: "【電子書籍版】インクルーシブHTML+CSS & JavaScript",
41
- publisher: "ボーンデジタル",
42
- url: "https://wgn-obs.shop-pro.jp/?pid=144269584",
43
- price: 2640,
44
- });
45
- });
46
-
47
- it("coverImageUrl が設定される", async () => {
48
- const body = await loadFixture("born-digital-search.html");
49
- const http = new MockHttpClient().addResponse(
50
- "https://wgn-obs.shop-pro.jp/",
51
- { status: 200, body },
52
- );
53
-
54
- const results = await bornDigitalAdapter.search({ title: "HTML" }, makeDeps(http));
55
-
56
- assert.strictEqual(
57
- results[0].coverImageUrl,
58
- "https://img07.shop-pro.jp/PA01427/945/product/167400957_th.png?cmsp_timestamp=20220328140327",
59
- );
60
- });
61
-
62
- it("ebookStores に ボーンデジタル (social DRM) が含まれる", async () => {
63
- const body = await loadFixture("born-digital-search.html");
64
- const http = new MockHttpClient().addResponse(
65
- "https://wgn-obs.shop-pro.jp/",
66
- { status: 200, body },
67
- );
68
-
69
- const results = await bornDigitalAdapter.search({ title: "HTML" }, makeDeps(http));
70
-
71
- assert.deepStrictEqual(results[0].ebookStores, [
72
- {
73
- name: "ボーンデジタル",
74
- url: "https://wgn-obs.shop-pro.jp/?pid=167400957",
75
- drm: "social",
76
- },
77
- ]);
78
- });
79
-
80
- it("title も author も空の場合は [] を返しHTTPを呼ばない", async () => {
81
- const http = new MockHttpClient();
82
-
83
- const results = await bornDigitalAdapter.search({}, makeDeps(http));
84
-
85
- assert.deepStrictEqual(results, []);
86
- assert.strictEqual(http.calls.length, 0);
87
- });
88
-
89
- it("検索リクエストに EUC-JP エンコードされた keyword が含まれる", async () => {
90
- const body = await loadFixture("born-digital-search.html");
91
- const http = new MockHttpClient().addResponse(
92
- "https://wgn-obs.shop-pro.jp/",
93
- { status: 200, body },
94
- );
95
-
96
- await bornDigitalAdapter.search({ title: "HTML" }, makeDeps(http));
97
-
98
- assert.ok(http.calls[0].includes("mode=srh"));
99
- // ASCII は EUC-JP でも同じバイト列だがパーセントエンコードされる
100
- assert.ok(http.calls[0].includes("keyword=%48%54%4D%4C"));
101
- });
102
- });
103
-
104
- describe("getDetail()", () => {
105
- it("詳細情報を返す", async () => {
106
- const body = await loadFixture("born-digital-detail.html");
107
- const http = new MockHttpClient().addResponse(
108
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
109
- { status: 200, body },
110
- );
111
-
112
- const book = await bornDigitalAdapter.getDetail(
113
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
114
- makeDeps(http),
115
- );
116
-
117
- assert.partialDeepStrictEqual(book, {
118
- title: "【PDFダウンロード版】HTML解体新書 ー仕様から紐解く本格入門",
119
- publisher: "ボーンデジタル",
120
- price: 3520,
121
- publishedAt: "2022-04-07",
122
- });
123
- });
124
-
125
- it("著者を取得する", async () => {
126
- const body = await loadFixture("born-digital-detail.html");
127
- const http = new MockHttpClient().addResponse(
128
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
129
- { status: 200, body },
130
- );
131
-
132
- const book = await bornDigitalAdapter.getDetail(
133
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
134
- makeDeps(http),
135
- );
136
-
137
- assert.deepStrictEqual(book.authors, ["太田 良典", "中村 直樹"]);
138
- });
139
-
140
- it("Colorme JSON から価格を取得する", async () => {
141
- const body = await loadFixture("born-digital-detail.html");
142
- const http = new MockHttpClient().addResponse(
143
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
144
- { status: 200, body },
145
- );
146
-
147
- const book = await bornDigitalAdapter.getDetail(
148
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
149
- makeDeps(http),
150
- );
151
-
152
- assert.strictEqual(book.price, 3520);
153
- });
154
-
155
- it("coverImageUrl が設定される", async () => {
156
- const body = await loadFixture("born-digital-detail.html");
157
- const http = new MockHttpClient().addResponse(
158
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
159
- { status: 200, body },
160
- );
161
-
162
- const book = await bornDigitalAdapter.getDetail(
163
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
164
- makeDeps(http),
165
- );
166
-
167
- assert.strictEqual(
168
- book.coverImageUrl,
169
- "https://img07.shop-pro.jp/PA01427/945/product/167400957.png?cmsp_timestamp=20220328140327",
170
- );
171
- });
172
-
173
- it("ebookStores に ボーンデジタル (social DRM) が含まれる", async () => {
174
- const body = await loadFixture("born-digital-detail.html");
175
- const http = new MockHttpClient().addResponse(
176
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
177
- { status: 200, body },
178
- );
179
-
180
- const book = await bornDigitalAdapter.getDetail(
181
- "https://wgn-obs.shop-pro.jp/?pid=167400957",
182
- makeDeps(http),
183
- );
184
-
185
- assert.deepStrictEqual(book.ebookStores, [
186
- {
187
- name: "ボーンデジタル",
188
- url: "https://wgn-obs.shop-pro.jp/?pid=167400957",
189
- drm: "social",
190
- },
191
- ]);
192
- });
193
- });
194
- });
@@ -1,207 +0,0 @@
1
- import { describe, it } from "node:test";
2
- import assert from "node:assert/strict";
3
- import { readFile } from "node:fs/promises";
4
- import { join } from "node:path";
5
- import { coronashaAdapter } from "../../../../src/adapters/publishers/coronasha.js";
6
- import { MockHttpClient } from "../../../../src/adapters/http/mock-client.js";
7
- import { CheerioHtmlParser } from "../../../../src/adapters/html/cheerio-parser.js";
8
- import { NullCacheStore } from "../../../../src/adapters/cache/null-cache.js";
9
-
10
- const FIXTURES_DIR = join(import.meta.dirname, "../../../fixtures");
11
-
12
- function makeDeps(http: MockHttpClient) {
13
- return { http, parser: new CheerioHtmlParser(), cache: new NullCacheStore() };
14
- }
15
-
16
- async function loadFixture(name: string): Promise<string> {
17
- return readFile(join(FIXTURES_DIR, name), "utf-8");
18
- }
19
-
20
- describe("coronashaAdapter", () => {
21
- describe("search()", () => {
22
- it("電子版ありの書籍のみ返す(電子版なしは除外)", async () => {
23
- const body = await loadFixture("coronasha-search.html");
24
- const http = new MockHttpClient().addResponse(
25
- "https://www.coronasha.co.jp/np/result.html",
26
- { status: 200, body },
27
- );
28
-
29
- const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
30
-
31
- // フィクスチャには電子版あり1件・電子版なし1件あり、電子版のみ返す
32
- assert.strictEqual(results.length, 1);
33
- });
34
-
35
- it("タイトルを tunogaki + book-title で組み立てる", async () => {
36
- const body = await loadFixture("coronasha-search.html");
37
- const http = new MockHttpClient().addResponse(
38
- "https://www.coronasha.co.jp/np/result.html",
39
- { status: 200, body },
40
- );
41
-
42
- const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
43
-
44
- assert.strictEqual(results[0].title, "1から始める Juliaプログラミング大全");
45
- });
46
-
47
- it("著者一覧を返す", async () => {
48
- const body = await loadFixture("coronasha-search.html");
49
- const http = new MockHttpClient().addResponse(
50
- "https://www.coronasha.co.jp/np/result.html",
51
- { status: 200, body },
52
- );
53
-
54
- const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
55
-
56
- assert.deepStrictEqual(results[0].authors, ["進藤 裕之", "佐藤 建太"]);
57
- });
58
-
59
- it("価格・ISBN・発行日を返す", async () => {
60
- const body = await loadFixture("coronasha-search.html");
61
- const http = new MockHttpClient().addResponse(
62
- "https://www.coronasha.co.jp/np/result.html",
63
- { status: 200, body },
64
- );
65
-
66
- const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
67
-
68
- assert.partialDeepStrictEqual(results[0], {
69
- price: 3630,
70
- isbn: "9784339029345",
71
- publishedAt: "2023-05-01",
72
- });
73
- });
74
-
75
- it("coverImageUrl が絶対URLになる", async () => {
76
- const body = await loadFixture("coronasha-search.html");
77
- const http = new MockHttpClient().addResponse(
78
- "https://www.coronasha.co.jp/np/result.html",
79
- { status: 200, body },
80
- );
81
-
82
- const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
83
-
84
- assert.strictEqual(
85
- results[0].coverImageUrl,
86
- "https://www.coronasha.co.jp/np/images/isbn/9784339029345_main.jpg",
87
- );
88
- });
89
-
90
- it("publisher が コロナ社 になる", async () => {
91
- const body = await loadFixture("coronasha-search.html");
92
- const http = new MockHttpClient().addResponse(
93
- "https://www.coronasha.co.jp/np/result.html",
94
- { status: 200, body },
95
- );
96
-
97
- const results = await coronashaAdapter.search({ title: "Julia" }, makeDeps(http));
98
-
99
- assert.strictEqual(results[0].publisher, "コロナ社");
100
- });
101
-
102
- it("title も author も空の場合は [] を返しHTTPを呼ばない", async () => {
103
- const http = new MockHttpClient();
104
-
105
- const results = await coronashaAdapter.search({}, makeDeps(http));
106
-
107
- assert.deepStrictEqual(results, []);
108
- assert.strictEqual(http.calls.length, 0);
109
- });
110
- });
111
-
112
- describe("getDetail()", () => {
113
- it("詳細情報を返す", async () => {
114
- const body = await loadFixture("coronasha-detail.html");
115
- const http = new MockHttpClient().addResponse(
116
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
117
- { status: 200, body },
118
- );
119
-
120
- const book = await coronashaAdapter.getDetail(
121
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
122
- makeDeps(http),
123
- );
124
-
125
- assert.partialDeepStrictEqual(book, {
126
- title: "1から始める Juliaプログラミング大全",
127
- publisher: "コロナ社",
128
- price: 3630,
129
- publishedAt: "2023-05-01",
130
- isbn: "9784339029345",
131
- });
132
- });
133
-
134
- it("著者一覧を返す", async () => {
135
- const body = await loadFixture("coronasha-detail.html");
136
- const http = new MockHttpClient().addResponse(
137
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
138
- { status: 200, body },
139
- );
140
-
141
- const book = await coronashaAdapter.getDetail(
142
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
143
- makeDeps(http),
144
- );
145
-
146
- assert.deepStrictEqual(book.authors, ["進藤 裕之", "佐藤 建太"]);
147
- });
148
-
149
- it("ebookStores に Kindle・Kinoppy・VarsityWave eBooks が含まれる", async () => {
150
- const body = await loadFixture("coronasha-detail.html");
151
- const http = new MockHttpClient().addResponse(
152
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
153
- { status: 200, body },
154
- );
155
-
156
- const book = await coronashaAdapter.getDetail(
157
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
158
- makeDeps(http),
159
- );
160
-
161
- for (const { name, drm } of [
162
- { name: "Kindle", drm: "drm" },
163
- { name: "Kinoppy", drm: "drm" },
164
- { name: "VarsityWave eBooks", drm: "drm" },
165
- ]) {
166
- assert.ok(
167
- book.ebookStores.some(s => s.name === name && s.drm === drm),
168
- `Expected store: ${name} (${drm})`,
169
- );
170
- }
171
- });
172
-
173
- it("Knowledge Worker (kw.maruzen.co.jp) は ebookStores に含まれない", async () => {
174
- const body = await loadFixture("coronasha-detail.html");
175
- const http = new MockHttpClient().addResponse(
176
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
177
- { status: 200, body },
178
- );
179
-
180
- const book = await coronashaAdapter.getDetail(
181
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
182
- makeDeps(http),
183
- );
184
-
185
- const names = book.ebookStores.map(s => s.name);
186
- assert.ok(!names.includes("Knowledge Worker"));
187
- });
188
-
189
- it("coverImageUrl が絶対URLになる", async () => {
190
- const body = await loadFixture("coronasha-detail.html");
191
- const http = new MockHttpClient().addResponse(
192
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
193
- { status: 200, body },
194
- );
195
-
196
- const book = await coronashaAdapter.getDetail(
197
- "https://www.coronasha.co.jp/np/isbn/9784339029345/",
198
- makeDeps(http),
199
- );
200
-
201
- assert.strictEqual(
202
- book.coverImageUrl,
203
- "https://www.coronasha.co.jp/np/images/isbn/9784339029345_main.jpg",
204
- );
205
- });
206
- });
207
- });