diff --git a/test/data/xmltv/fixtures/invalid.json b/test/data/xmltv/fixtures/invalid.json new file mode 100644 index 00000000..9992912b --- /dev/null +++ b/test/data/xmltv/fixtures/invalid.json @@ -0,0 +1,14 @@ +{ + "services": [ + { + "id": 12346, + "networkId": 1, + "serviceId": 102, + "name": "非表示チャンネル", + "type": 0, + "logoId": -1, + "remoteControlKeyId": 6 + } + ], + "programs": [] +} diff --git a/test/data/xmltv/fixtures/logo.json b/test/data/xmltv/fixtures/logo.json new file mode 100644 index 00000000..dca752a3 --- /dev/null +++ b/test/data/xmltv/fixtures/logo.json @@ -0,0 +1,14 @@ +{ + "services": [ + { + "id": 54321, + "networkId": 2, + "serviceId": 202, + "name": "ロゴチャンネル", + "type": 173, + "logoId": 10, + "remoteControlKeyId": 7 + } + ], + "programs": [] +} diff --git a/test/data/xmltv/fixtures/valid.json b/test/data/xmltv/fixtures/valid.json new file mode 100644 index 00000000..27d89a43 --- /dev/null +++ b/test/data/xmltv/fixtures/valid.json @@ -0,0 +1,62 @@ +{ + "services": [ + { + "id": 12345, + "networkId": 1, + "serviceId": 101, + "name": "テストチャンネル & Co.", + "type": 1, + "logoId": -1, + "remoteControlKeyId": 5 + }, + { + "id": 12346, + "networkId": 1, + "serviceId": 102, + "name": "サブチャンネル", + "type": 1, + "logoId": -1, + "remoteControlKeyId": 5 + }, + { + "id": 12347, + "networkId": 1, + "serviceId": 999, + "name": "キーなしチャンネル", + "type": 173, + "logoId": -1 + } + ], + "programs": [ + { + "id": 1000, + "networkId": 1, + "serviceId": 101, + "startAt": 1610000000000, + "duration": 3600000, + "name": "テスト番組 <こんにちは>", + "description": "テスト説明 & \"引用\" 'シングル'", + "genres": [ + { "lv1": 0, "lv2": 0, "un1": 0, "un2": 0 }, + { "lv1": 0, "lv2": 0, "un1": 0, "un2": 0 }, + { "lv1": 14, "lv2": 1, "un1": 2, "un2": 3 }, + { "lv1": 14, "lv2": 9, "un1": 9, "un2": 9 } + ] + }, + { + "id": 1001, + "networkId": 1, + "serviceId": 102, + "startAt": 1610003600000, + "duration": 1800000 + }, + { + "id": 1002, + "networkId": 1, + "serviceId": 9991, + "startAt": 1610005400000, + "duration": 1800000, + "name": "存在しないサービス" + } + ] +} diff --git a/test/data/xmltv/goldens/empty.xml b/test/data/xmltv/goldens/empty.xml new file mode 100644 index 00000000..59fc074b --- /dev/null +++ b/test/data/xmltv/goldens/empty.xml @@ -0,0 +1,4 @@ + + + + diff --git a/test/data/xmltv/goldens/logo.xml b/test/data/xmltv/goldens/logo.xml new file mode 100644 index 00000000..732fa54e --- /dev/null +++ b/test/data/xmltv/goldens/logo.xml @@ -0,0 +1,8 @@ + + + + +ロゴチャンネル +7.1 + + diff --git a/test/data/xmltv/goldens/valid.xml b/test/data/xmltv/goldens/valid.xml new file mode 100644 index 00000000..528d795b --- /dev/null +++ b/test/data/xmltv/goldens/valid.xml @@ -0,0 +1,26 @@ + + + + +テストチャンネル & Co. +5.1 + + +サブチャンネル +5.2 + + +キーなしチャンネル +999.1 + + +テスト番組 <こんにちは> +テスト説明 & "引用" 'シングル' +ニュース/報道 - 定時・総合 +邦画 - サスペンス/ミステリー + + + + + + diff --git a/test/xmltv.spec.js b/test/xmltv.spec.js new file mode 100644 index 00000000..62a41724 --- /dev/null +++ b/test/xmltv.spec.js @@ -0,0 +1,122 @@ +const { describe, it, beforeEach, afterEach } = require("node:test"); +const assert = require("assert"); +const fs = require("fs"); +const path = require("path"); + +// Mock dependencies before requiring the module +const _ = require("../lib/Mirakurun/_").default; +const ServiceClass = require("../lib/Mirakurun/Service").default; + +// The file to test +const xmltv = require("../lib/Mirakurun/api/iptv/xmltv"); + +class MockResponse { + constructor() { + this.headers = {}; + this.statusCode = 200; + this.body = ""; + } + setHeader(name, value) { + this.headers[name] = value; + } + status(code) { + this.statusCode = code; + } + end(data) { + this.body += data; + } +} + +function loadFixture(name) { + const jsonPath = path.join(__dirname, "data", "xmltv", "fixtures", `${name}.json`); + const data = JSON.parse(fs.readFileSync(jsonPath, "utf8")); + + // Add getOrder function to services + data.services.forEach((s, i) => { + s.getOrder = () => i + 1; + }); + + return data; +} + +function loadGolden(name) { + const xmlPath = path.join(__dirname, "data", "xmltv", "goldens", `${name}.xml`); + return fs.readFileSync(xmlPath, "utf8").trim(); +} + +describe("api/iptv/xmltv", () => { + let originalIsLogoDataExists; + + beforeEach(() => { + _.service = { + items: [], + get: (networkId, serviceId) => { + return _.service.items.find(s => s.networkId === networkId && s.serviceId === serviceId) || null; + } + }; + _.program = { + itemMap: new Map() + }; + + originalIsLogoDataExists = ServiceClass.isLogoDataExists; + ServiceClass.isLogoDataExists = async (networkId, logoId) => false; + }); + + afterEach(() => { + ServiceClass.isLogoDataExists = originalIsLogoDataExists; + }); + + it("should generate empty XMLTV when no services or programs exist", async () => { + const req = { protocol: "http", headers: { host: "localhost:40772" } }; + const res = new MockResponse(); + + await xmltv.get(req, res); + + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.headers["Content-Type"], "text/xml; charset=utf-8"); + assert.strictEqual(res.body, loadGolden("empty")); + }); + + it("should generate XMLTV with a valid service and program", async () => { + const req = { protocol: "http", headers: { host: "localhost:40772" } }; + const res = new MockResponse(); + + const data = loadFixture("valid"); + _.service.items = data.services; + for (const prog of data.programs) { + _.program.itemMap.set(prog.id, prog); + } + + await xmltv.get(req, res); + + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.body, loadGolden("valid")); + }); + + it("should ignore services that are not type 1 or 173", async () => { + const req = { protocol: "http", headers: { host: "localhost:40772" } }; + const res = new MockResponse(); + + const data = loadFixture("invalid"); + _.service.items = data.services; + + await xmltv.get(req, res); + + assert.strictEqual(res.statusCode, 200); + assert.strictEqual(res.body, loadGolden("empty")); + }); + + it("should include logo tags if Service.isLogoDataExists is true", async () => { + ServiceClass.isLogoDataExists = async (networkId, logoId) => networkId === 2 && logoId === 10; + + const req = { protocol: "http", headers: { host: "localhost:40772" } }; + const res = new MockResponse(); + + const data = loadFixture("logo"); + _.service.items = data.services; + + await xmltv.get(req, res); + + assert.strictEqual(res.body, loadGolden("logo")); + }); +});