get tests passing in the browser

This commit is contained in:
ennmichael
2023-04-05 16:00:36 +02:00
parent b650a1684f
commit 2544d79975
21 changed files with 495 additions and 50 deletions

View File

@ -3,11 +3,31 @@ services:
well-known:
build: ./docker/well-known
restart: on-failure
ports:
- 12647:80
relay:
build: ./docker/relay
restart: on-failure
well-known-proxy:
build: ./docker/cors-proxy
restart: on-failure
environment:
TARGET: "well-known:80"
ports:
- 12648:8080
- 12649:8000
- 12647:80
relay-proxy:
build: ./docker/cors-proxy
restart: on-failure
environment:
TARGET: "relay:8080"
ports:
- 12648:80
relay-restart-proxy:
build: ./docker/cors-proxy
restart: on-failure
environment:
TARGET: "relay:8000"
ports:
- 12649:80

View File

@ -0,0 +1,4 @@
# An nginx proxy which adds "Access-Control-Allow-Origin: *" to responses.
FROM nginx
COPY config.sh /docker-entrypoint.d/

View File

@ -0,0 +1,24 @@
echo "\
map \$http_upgrade \$connection_upgrade {\
default upgrade;\
'' close;\
}\
\
proxy_read_timeout 600s;\
\
server {\
listen 80;\
server_name default;\
\
location / {\
proxy_pass http://$TARGET;\
proxy_http_version 1.1;\
proxy_set_header Upgrade \$http_upgrade;\
proxy_set_header Connection \$connection_upgrade;\
# The NIP defines that the relay should return Access-Control-Allow-Origin: * here, so don't do it twice.\n\
if (\$http_accept != 'application/nostr+json') {\
add_header 'Access-Control-Allow-Origin' '*';\
}\
}\
}" > /etc/nginx/conf.d/default.conf
cat /etc/nginx/conf.d/default.conf

View File

@ -1 +1,2 @@
Dockerfile
node_modules/

View File

@ -5,6 +5,7 @@ RUN apt-get update && apt-get install -y curl nodejs npm
RUN npm i -g yarn
EXPOSE 8000
EXPOSE 8080
COPY . .
USER $APP_USER

View File

@ -1,23 +1,25 @@
{
"name": "@snort/nostr",
"version": "1.0.0",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
"scripts": {
"build": "tsc",
"watch": "tsc -w",
"test": "ts-mocha --type-check -j 1 --timeout 5s test/*.ts",
"build": "webpack",
"watch": "webpack -w",
"test": "ts-mocha --type-check -j 1 --timeout 5s test/test.*.ts",
"test-browser": "ts-node test/browser/server.ts",
"lint": "eslint ."
},
"devDependencies": {
"@types/events": "^3.0.0",
"@types/expect": "^24.3.0",
"@types/express": "^4.17.17",
"@types/mocha": "^10.0.1",
"@typescript-eslint/eslint-plugin": "^5.53.0",
"@typescript-eslint/parser": "^5.53.0",
"eslint": "^8.34.0",
"express": "^4.18.2",
"mocha": "^10.2.0",
"ts-mocha": "^10.0.0",
"ts-node": "^10.9.1",
"typescript": "^4.9.5"
},
"prettier": {
@ -26,10 +28,15 @@
"dependencies": {
"@noble/hashes": "^1.2.0",
"@noble/secp256k1": "^1.7.1",
"@types/chai": "^4.3.4",
"base64-js": "^1.5.1",
"bech32": "^2.0.0",
"chai": "^4.3.7",
"events": "^3.3.0",
"isomorphic-ws": "^5.0.0",
"ts-loader": "^9.4.2",
"webpack": "^5.77.0",
"webpack-cli": "^5.0.1",
"ws": "^8.12.1"
},
"directories": {

View File

@ -112,7 +112,7 @@ export async function aesEncryptBase64(
sharedKey,
{ name: "AES-CBC" },
false,
["encrypt", "decrypt"]
["encrypt"]
)
const iv = window.crypto.getRandomValues(new Uint8Array(16))
const data = new TextEncoder().encode(plaintext)
@ -133,15 +133,10 @@ export async function aesEncryptBase64(
const iv = crypto.randomFillSync(new Uint8Array(16))
const cipher = crypto.createCipheriv(
"aes-256-cbc",
// TODO If this code is correct, also fix the example code
// TODO I also this that the slice() above is incorrect because the author
// thought this was hex but it's actually bytes so should take 32 bytes not 64
// TODO Actually it's probably cleanest to leave out the end of the slice completely, if possible, and it should be
Buffer.from(sharedKey),
iv
)
let encrypted = cipher.update(plaintext, "utf8", "base64")
// TODO Could save an allocation here by avoiding the +=
encrypted += cipher.final("base64")
return {
data: encrypted,
@ -158,8 +153,24 @@ export async function aesDecryptBase64(
const sharedPoint = secp.getSharedSecret(recipient, "02" + sender)
const sharedKey = sharedPoint.slice(1, 33)
if (typeof window === "object") {
// TODO Can copy this from the legacy code
throw new NostrError("todo")
const decodedData = base64.toByteArray(data)
const decodedIv = base64.toByteArray(iv)
const importedKey = await window.crypto.subtle.importKey(
"raw",
sharedKey,
{ name: "AES-CBC" },
false,
["decrypt"]
)
const plaintext = await window.crypto.subtle.decrypt(
{
name: "AES-CBC",
iv: decodedIv,
},
importedKey,
decodedData
)
return new TextDecoder().decode(plaintext)
} else {
const crypto = await import("crypto")
const decipher = crypto.createDecipheriv(

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Tests</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://unpkg.com/mocha/mocha.css" />
</head>
<body>
<div id="mocha"></div>
<script src="https://unpkg.com/mocha/mocha.js"></script>
<script class="mocha-init">
mocha.setup({
ui: "bdd",
timeout: "5s",
})
mocha.checkLeaks()
</script>
<!-- The server replaces the following line with <script> tags for all tests. -->
<!-- TESTS -->
<script class="mocha-exec">
mocha.run()
</script>
</body>
</html>

View File

@ -0,0 +1,45 @@
/**
* Serve tests in the browser.
*/
import express from "express"
import * as path from "path"
import * as fs from "fs"
const port = 33543
const app = express()
app.use("/", (req: express.Request, res: express.Response) => {
if (req.path === "/") {
const index = fs.readFileSync(path.join(__dirname, "index.html"), {
encoding: "utf8",
})
const tests = fs
.readdirSync(path.join(__dirname, "..", "..", "dist", "test"))
.filter(
(f) =>
f.startsWith("test.") && !f.endsWith(".map") && !f.endsWith(".d.ts")
)
.map((src) => `<script src="${src}"></script>`)
.join("\n")
res.set("Content-Type", "text/html")
res.send(index.replace("<!-- TESTS -->", tests))
res.end()
} else if (req.path === "/favicon.ico") {
res.status(404)
res.end()
} else {
const file = path.join(__dirname, "..", "..", "dist", "test", req.path)
res.sendFile(file, (err) => {
if (err) {
console.error(err)
res.status(404)
}
res.end()
})
}
})
app.listen(port, () => {
console.log(`Browser tests: http://localhost:${port}`)
})

View File

@ -69,12 +69,8 @@ export async function setup(
}
async function restartRelay() {
// Make a request to the endpoint which will crash the process and cause it to restart.
try {
await fetch("http://localhost:12649")
} catch (e) {
// Since the process exits, an error is expected.
}
// Make a request to the endpoint which will exit the process and cause it to restart.
await fetch("http://localhost:12649")
// Wait until the relay process is ready.
for (;;) {

View File

@ -1,4 +1,4 @@
import assert from "assert"
import { assert } from "chai"
import { EventKind } from "../src/event"
import { createContactList } from "../src/event/contact-list"
import { setup } from "./setup"

View File

@ -1,6 +1,6 @@
import { assert } from "chai"
import { EventKind } from "../src/event"
import { parsePublicKey } from "../src/crypto"
import assert from "assert"
import { setup } from "./setup"
import { createTextNote } from "../src/event/text"
import { createDeletion } from "../src/event/deletion"

View File

@ -1,6 +1,6 @@
import { assert } from "chai"
import { EventKind } from "../src/event"
import { parsePublicKey } from "../src/crypto"
import assert from "assert"
import { setup } from "./setup"
import { createDirectMessage } from "../src/event/direct-message"

View File

@ -1,4 +1,4 @@
import assert from "assert"
import { assert } from "chai"
import { defined } from "../src/common"
import { EventKind } from "../src/event"
import { createSetMetadata } from "../src/event/set-metadata"

View File

@ -1,4 +1,4 @@
import assert from "assert"
import { assert } from "chai"
import { Nostr } from "../src/client"
import { relayUrl } from "./setup"

View File

@ -1,4 +1,4 @@
import assert from "assert"
import { assert } from "chai"
import { Nostr } from "../src/client"
import { setup } from "./setup"

View File

@ -1,6 +1,6 @@
import { assert } from "chai"
import { EventKind } from "../src/event"
import { parsePublicKey } from "../src/crypto"
import assert from "assert"
import { setup } from "./setup"
import { createSetMetadata } from "../src/event/set-metadata"

View File

@ -1,6 +1,6 @@
import { assert } from "chai"
import { EventKind } from "../src/event"
import { parsePublicKey } from "../src/crypto"
import assert from "assert"
import { setup } from "./setup"
import { createTextNote } from "../src/event/text"

View File

@ -1,17 +1,15 @@
{
"compilerOptions": {
"target": "ES2015",
"target": "ES2020",
"moduleResolution": "node",
"esModuleInterop": true,
"noImplicitOverride": true,
"module": "CommonJS",
"strict": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"composite": true,
"outDir": "dist",
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"noImplicitOverride": true,
"module": "CommonJS",
"strict": true
"outDir": "dist"
},
"include": ["src"]
"include": ["src", "test"]
}

View File

@ -0,0 +1,31 @@
const fs = require("fs")
const entry = {
lib: "./src/index.ts",
}
for (const file of fs.readdirSync("./test/")) {
if (/.ts$/.test(file)) {
const name = file.replace(/.ts$/, "")
entry[`test/${name}`] = `./test/${file}`
}
}
module.exports = {
mode: process.env.NODE_ENV || "development",
devtool: "inline-source-map",
entry,
resolve: {
extensions: [".ts", ".js"],
fallback: {
crypto: false,
},
},
module: {
rules: [{ test: /\.ts$/, use: "ts-loader" }],
},
output: {
filename: "[name].js",
path: `${__dirname}/dist`,
},
}