Skip to content

Commit

Permalink
first
Browse files Browse the repository at this point in the history
  • Loading branch information
wydengyre committed Jan 24, 2024
0 parents commit 7df8330
Show file tree
Hide file tree
Showing 25 changed files with 11,232 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
charset = utf-8
indent_style = tab
indent_size = 2
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: daily
- package-ecosystem: npm
directory: web
schedule:
interval: daily
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Continuous Integration

on:
workflow_dispatch:
push:
branches:
- master
tags:
- '*'
pull_request:
types:
- opened
- reopened
- synchronize
- ready_for_review

concurrency:
group: '${{ github.workflow }}-${{ !contains(github.event.pull_request.labels.*.name, ''ci-test-flaky'') && github.head_ref || github.run_id }}'
cancel-in-progress: true

jobs:
ci:
timeout-minutes: 2
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
with:
node-version-file: '.node-version'
cache: 'npm'
- name: Install npm dependencies
run: npm ci
- name: Build and test
run: npm run ci
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.idea
*.iml
node_modules
dist
cf/.wrangler
1 change: 1 addition & 0 deletions .node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
21.6.1
16 changes: 16 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://biomejs.dev/schemas/1.5.1/schema.json",
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"files": {
"include": ["cf/*.ts", "lib/*.ts", "testdata/*.parsed.json", "testdata/*.pretty.json"],
"ignore": ["cf/.wrangler"]
}
}
3 changes: 3 additions & 0 deletions cf/.dev.vars
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
BASE_URL=https://test.dev/
RAI_BASE_URL=http://localhost:8091/
FETCH_QUEUE_SIZE=5
11 changes: 11 additions & 0 deletions cf/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "@tsconfig/node21/tsconfig.json",
"compilerOptions": {
"module": "nodenext",
"noEmit": true,
"resolveJsonModule": true,
"types": ["@cloudflare/workers-types"]
},
"include": ["*.ts", "../lib/declarations.d.ts"]
}
204 changes: 204 additions & 0 deletions cf/worker.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import { Server, createServer } from "http";
import { strict as assert } from "node:assert";
import { readFile } from "node:fs/promises";
import path from "node:path";
import test, { after, before } from "node:test";
import { fileURLToPath } from "node:url";
import { getPodcastFromFeed } from "@podverse/podcast-feed-parser";
import { createServerAdapter } from "@whatwg-node/server";
import { Router, RouterType, error, json } from "itty-router";
import { UnstableDevWorker, unstable_dev } from "wrangler";
import feedJson from "../testdata/lastoriaingiallo.json" with { type: "json" };
import expectedJson from "../testdata/lastoriaingiallo.parsed.json" with {
type: "json",
};

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const englishHtmlPath = path.join(__dirname, "../lib/english.html");
const italianHtmlPath = path.join(__dirname, "../lib/italian.html");

let worker: UnstableDevWorker;

before(async () => {
const experimental = { disableExperimentalWarning: true };
worker = await unstable_dev("cf/worker.ts", {
// uncomment and change level for help debugging
// logLevel: "info",
experimental,
});
});

after(async () => {
await worker.stop();
});

test("english index", async () => {
const englishIndex = await readFile(englishHtmlPath, "utf8");
const resp = await worker.fetch();
const text = await resp.text();

assert(resp.ok);
assert.strictEqual(resp.status, 200);
assert.strictEqual(resp.statusText, "OK");
assert.strictEqual(resp.headers.get("Content-Language"), "en");
assert.strictEqual(text, englishIndex);
});

test("italian index", async () => {
const italianIndex = await readFile(italianHtmlPath, "utf8");
const resp = await worker.fetch("", { headers: { "Accept-Language": "it" } });
const text = await resp.text();

assert(resp.ok);
assert.strictEqual(resp.status, 200);
assert.strictEqual(resp.statusText, "OK");
assert.strictEqual(resp.headers.get("Content-Language"), "it");
assert.strictEqual(text, italianIndex);
});

test("rss feed success", async (t) => {
const router = Router();
router.get("/programmi/lastoriaingiallo.json", () => {
return json(feedJson);
});
router.head(
"*",
() =>
new Response(null, {
status: 200,
headers: {
"Content-Type": "audio/mpeg",
"Content-Length": "123456789",
},
}),
);
await using _server = await MockRaiServer.create(router);

const resp = await worker.fetch("/programmi/lastoriaingiallo.xml");
assert(resp.ok);
assert.strictEqual(resp.status, 200);

const feed = await resp.text();
const parsedFeed = JSON.parse(JSON.stringify(getPodcastFromFeed(feed)));

const trulyExpectedJSON = replaceImageUrls(
replaceMediaUrls(stripLastBuildDate(expectedJson)),
);
trulyExpectedJSON.meta.imageURL = trulyExpectedJSON.meta.imageURL.replace(
/^https:\/\/test.dev\//,
"http://localhost:8091/",
);
assert.deepStrictEqual(stripLastBuildDate(parsedFeed), trulyExpectedJSON);
});

test("rss feed failure: 404 from RAI server", async () => {
const resp = await worker.fetch("/theserverisntevenrunning.xml");
assert(!resp.ok);
assert.strictEqual(resp.status, 404);
});

test("rss feed failure: 500 from RAI server", async () => {
const router = Router();
router.get("/programmi/500.json", () => {
return error(500, "RAI server exploded");
});
await using _ = await MockRaiServer.create(router);

const resp = await worker.fetch("/programmi/500.xml");
assert(!resp.ok);
assert.strictEqual(resp.status, 500);
});

test("rss feed failure: failure to process RAI json feed", async () => {
const router = Router();
router.get("/programmi/lastoriaingiallo.json", () => {
return json({ foo: "bar" });
});
await using _server = await MockRaiServer.create(router);

const resp = await worker.fetch("/programmi/lastoriaingiallo.xml");
assert(!resp.ok);
assert.strictEqual(resp.status, 500);
assert.strictEqual(resp.statusText, "Internal Server Error");
const text = await resp.text();
assert.strictEqual(
text,
'{"status":500,"error":"error converting upstream json feed from RAI"}',
);
});

test("404", async () => {
const resp = await worker.fetch("foo");

assert(!resp.ok);
assert.strictEqual(resp.status, 404);
assert.strictEqual(resp.statusText, "Not Found");
const text = await resp.text();
assert.strictEqual(text, "Not found.");
});

class MockRaiServer {
#server: Server;

private constructor(server: Server) {
this.#server = server;
}

static async create(router: RouterType): Promise<MockRaiServer> {
const { RAI_BASE_URL } = process.env;
if (RAI_BASE_URL === undefined) {
throw new Error("RAI_BASE_URL is undefined");
}
const url = new URL(RAI_BASE_URL);
const listenPort = parseInt(url.port);

const ittyServer = createServerAdapter(router);
const httpServer = createServer(ittyServer);
await new Promise<void>((resolve) =>
httpServer.listen(listenPort, url.hostname, resolve),
);
return new MockRaiServer(httpServer);
}

[Symbol.asyncDispose](): Promise<void> {
return this.#server[Symbol.asyncDispose]();
}
}

type ExpectedJson = typeof expectedJson;
type DatelessMeta = Omit<typeof expectedJson.meta, "lastBuildDate">;
type ExpectedJsonWithoutBuildDate = Omit<ExpectedJson, "meta"> & {
meta: DatelessMeta;
};
function stripLastBuildDate({
meta,
...rest
}: ExpectedJson): ExpectedJsonWithoutBuildDate {
const { lastBuildDate, ...metaRest } = meta;
return { meta: metaRest, ...rest };
}

function replaceMediaUrls(
j: ExpectedJsonWithoutBuildDate,
): ExpectedJsonWithoutBuildDate {
for (const e of j.episodes) {
e.enclosure.url = e.enclosure.url.replace(
/^https:\/\/media.test.dev\/(.+)\.mp3$/,
(_, p1) =>
`http://localhost:8091/relinker/relinkerServlet.htm?cont=${p1}`,
);
}
return j;
}
function replaceImageUrls(
j: ExpectedJsonWithoutBuildDate,
): ExpectedJsonWithoutBuildDate {
for (const e of j.episodes) {
e.imageURL = e.imageURL.replace(
/^https:\/\/test.dev\//,
"http://localhost:8091/",
);
}
return j;
}
5 changes: 5 additions & 0 deletions cf/worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Env, fetchHandler } from "../lib/router.js";

export default (<ExportedHandler<Env>>{
fetch: (request, env, _ctx) => fetchHandler(request, env),
});
9 changes: 9 additions & 0 deletions cf/wrangler.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
name = "raiplayrss"
main = "./worker.ts"
compatibility_date = "2024-01-16"
node_compat = true

[env.prod.vars]
BASE_URL = "https://raiplayrss.workers.dev/"
RAI_BASE_URL = "https://www.raiplaysound.it/"
FETCH_QUEUE_SIZE = 5
6 changes: 6 additions & 0 deletions lib/declarations.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
declare module "*.html" {
const content: string;
export default content;
}

declare module "@podverse/podcast-feed-parser";
12 changes: 12 additions & 0 deletions lib/english.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>raiplayrss</title>
</head>
<body>
<h1>raiplayrss</h1>
<p>having some fun</p>
</body>
</html>
Loading

0 comments on commit 7df8330

Please sign in to comment.