Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
djm2k committed Aug 11, 2024
1 parent 048f5fb commit 0f27404
Show file tree
Hide file tree
Showing 7 changed files with 1,615 additions and 213 deletions.
14 changes: 12 additions & 2 deletions babel.config.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
{
"presets": ["@babel/preset-typescript"]
}
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
],
"@babel/preset-typescript"
]
}
47 changes: 0 additions & 47 deletions docker/tests/container-util.js

This file was deleted.

102 changes: 102 additions & 0 deletions docker/tests/container-util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { writeFile } from 'fs/promises';
import { GenericContainer, Wait } from 'testcontainers';


Check failure on line 4 in docker/tests/container-util.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `⏎`
/**
* Start an `actual-server` from the root build context, using port 5006.
*/
export async function startActualContainer(buildContext = './') {
const newContainer = await GenericContainer

Check failure on line 9 in docker/tests/container-util.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `⏎····.fromDockerfile(buildContext)` with `.fromDockerfile(⏎····buildContext,`
.fromDockerfile(buildContext)
.build();

Check failure on line 11 in docker/tests/container-util.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `··` with `)`

return newContainer
.withExposedPorts(5006)
.withWaitStrategy(Wait.forListeningPorts())
.start();
}

/**
* Start a `caddy` instance with a generated Caddyfile.
*
* https://actualbudget.org/docs/config/reverse-proxies/#caddy
*/
export async function startCaddyContainer(actualServerPort: number):

Check failure on line 24 in docker/tests/container-util.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `actualServerPort:·number):⏎·` with `⏎··actualServerPort:·number,⏎):`
Promise<import('testcontainers').StartedTestContainer> {
if (typeof actualServerPort !== 'number') throw Error("actualServerPort must be number!");

Check failure on line 26 in docker/tests/container-util.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `·throw·Error("actualServerPort·must·be·number!"` with `⏎····throw·Error('actualServerPort·must·be·number!'`

// write Caddyfile to disk for copying
const source = './Caddyfile';
const testCaddyfileContents = 'http://localhost {\n\tencode gzip zstd\n' +

Check failure on line 30 in docker/tests/container-util.ts

View workflow job for this annotation

GitHub Actions / lint

Insert `⏎···`
'\treverse_proxy actual_server:' + actualServerPort.toString() + '\n}\n';

Check failure on line 31 in docker/tests/container-util.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `·actualServerPort.toString()·+` with `⏎····actualServerPort.toString()·+⏎···`
await writeFile(source, testCaddyfileContents);

const caddyContainer = new GenericContainer('caddy:latest')
.withCopyFilesToContainer([{ source, target: '/etc/caddyContainer/Caddyfile' }])

Check failure on line 35 in docker/tests/container-util.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `{·source,·target:·'/etc/caddyContainer/Caddyfile'·}` with `⏎······{·source,·target:·'/etc/caddyContainer/Caddyfile'·},⏎····`
.withExposedPorts(80)
.withWaitStrategy(Wait.forListeningPorts());

return caddyContainer.start();
}

// services:\
export async function startTraefikContainer(actualServerPort: number) {

Check failure on line 43 in docker/tests/container-util.ts

View workflow job for this annotation

GitHub Actions / lint

'actualServerPort' is defined but never used. Allowed unused args must match /^_/u
// write Caddyfile to disk for copying
const source = './traefik.yaml';
const testTraeFikYamlContents = `
logLevel: "DEBUG"
entryPoints:
web:
address: ":80"
providers:
docker: {}
`;
await writeFile(source, testTraeFikYamlContents);

const traefikContainer = new GenericContainer("traefik:latest")

Check failure on line 57 in docker/tests/container-util.ts

View workflow job for this annotation

GitHub Actions / lint

Replace `"traefik:latest"` with `'traefik:latest'`
.withExposedPorts(80)
.withCopyFilesToContainer([{ source, target: "/etc/traefik/traefik.yaml" }])
.withBindMounts([{ source: "/var/run/docker.sock", target: "/var/run/docker.sock" }])
.withWaitStrategy(Wait.forListeningPorts())


return traefikContainer.start();


}

/**
* Start an `actual-server` from the root build context, using port 5006.
*/
export async function startActualContainerWithTraefik(buildContext = './') {
const newContainer = await GenericContainer
.fromDockerfile(buildContext)
.build();

return newContainer.withLabels({
// "traefik.enable": "true",
"traefik.http.routers.actual-server.entrypoints": "web",
// "traefik.http.services.actual-server.loadbalancer.server.port": "5006"
})
.withExposedPorts(5006)
.withWaitStrategy(Wait.forListeningPorts())
.start();
}
// traefik:
// image: traefik:latest
// restart: unless-stopped
// ports:
// - "80:80"
// volumes:
// - "./traefik.yaml:/etc/traefik/traefik.yaml"
// - "./traefik/data:/data"
// - "/var/run/docker.sock:/var/run/docker.sock"

// actual-server:
// image: actualbudget/actual-server:latest-alpine
// restart: unless-stopped
// labels:

// volumes:
// - ./actual-data:/data
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { unlink } from 'fs/promises';
import request from 'supertest';
import { buildActionServer, startActualContainer, startCaddyContainer } from './container-util.js';
import { startActualContainer, startActualContainerWithTraefik, startCaddyContainer, startTraefikContainer } from './container-util.ts';

Check failure on line 3 in docker/tests/reverse-proxies.test.ts

View workflow job for this annotation

GitHub Actions / build

An import path cannot end with a '.ts' extension. Consider importing './container-util.js' instead.

// Requires testcontainers:
// `yarn add -D testcontainers`
Expand All @@ -24,46 +24,78 @@ import { buildActionServer, startActualContainer, startCaddyContainer } from './
// DEBUG=testcontainers* DOCKER_HOST=unix:///var/run/docker.sock yarn run e2e-test
// https://node.testcontainers.org/configuration/

let newActualServerBuild = await buildActionServer('./');
describe('Actual Server with Caddy', () => {
let actualServerContainer;
let caddyContainer;

beforeAll(async () => {
actualServerContainer = await startActualContainer(newActualServerBuild);
actualServerContainer = await startActualContainer();
caddyContainer = await startCaddyContainer(
actualServerContainer.getMappedPort(5006),
);
}, 66 * 1000);

it('should not allow login with no password', async () => {
it('should allow login', async () => {
const hostname = caddyContainer.getHost();
const port = caddyContainer.getMappedPort(80);
const caddyHost = `${hostname}:${port}`;
console.log('Caddy host: ' + caddyHost);
// console.log('Caddy host: ' + caddyHost);

const caddyRequest = request(caddyHost);
caddyRequest.post('/account/login').expect({"status":"error","reason":"invalid-password"}, (err) => {
throw err;

caddyRequest.post('/account/login').then(res => {
expect(res.statusCode).toBe(200)
});
});

afterAll(async () => {
if (caddyContainer) await caddyContainer.stop();
if (actualServerContainer) await actualServerContainer.stop();

// Delete Caddyfile from disk, if it exists
await unlink('./Caddyfile').catch((_err) => {
// silence ENOENT
// don't care about ENOENT
return;
});
});
});

// NGINX
describe('Actual Server with Traefik', () => {
let actualServerContainer;
let traefikContainer;

beforeAll(async () => {
actualServerContainer = await startActualContainerWithTraefik();
traefikContainer = await startTraefikContainer(
actualServerContainer.getMappedPort(5006),
);
}, 66 * 1000);

it('should allow login', async () => {
const hostname = traefikContainer.getHost();
const port = traefikContainer.getMappedPort(80);
const caddyHost = `${hostname}:${port}`;
// console.log('Caddy host: ' + caddyHost);

const caddyRequest = request(caddyHost);

caddyRequest.post('/account/login').then(res => {
expect(res.statusCode).toBe(200)
});
});

afterAll(async () => {
if (traefikContainer) await traefikContainer.stop();
if (actualServerContainer) await actualServerContainer.stop();

// Delete Caddyfile from disk, if it exists
await unlink('./traefik.yaml').catch((_err) => {
// don't care about ENOENT
return;
});
});
});



// TODO rename current implementation to allowedAuthCIDRs
// - populate it by default with whatever folks have set in their trustedProxies
// - we also need to fix the current implementation so it's not needed to be set if folks just want header auth, without restricting it
// - it SHOULD only need to be set if someone wants to lock header auth to a specific endpoint.
// TODO use trustedProxies for what you have here
//
12 changes: 9 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
"start": "node app",
"lint": "eslint . --max-warnings 0",
"build": "tsc",
"e2e-test": "DEBUG=testcontainers* NODE_ENV=test yarn node --experimental-vm-modules --no-warnings=ExperimentalWarning $(yarn bin jest) ./docker/tests --detectOpenHandles --forceExit --config {}",
"test": "NODE_ENV=test NODE_OPTIONS='--experimental-vm-modules --no-warnings=ExperimentalWarning --trace-warnings' jest --coverage",
"e2e-test": "yarn cross-env DEBUG='testcontainers*' NODE_ENV=test NODE_OPTIONS='--experimental-vm-modules --no-warnings=ExperimentalWarning --trace-warnings' jest ./docker/tests --detectOpenHandles --forceExit --config {}",
"test": "yarn cross-env NODE_ENV=test NODE_OPTIONS='--experimental-vm-modules --no-warnings=ExperimentalWarning --trace-warnings' jest --coverage",
"types": "tsc --noEmit --incremental",
"verify": "yarn lint && yarn types",
"reset-password": "node src/scripts/reset-password.js",
Expand All @@ -34,7 +34,11 @@
"uuid": "^9.0.0"
},
"devDependencies": {
"@babel/preset-typescript": "^7.20.2",
"@babel/core": "^7.24.9",
"@babel/preset-env": "^7.25.0",
"@babel/preset-typescript": "^7.24.7",
"@types/babel__core": "^7",
"@types/babel__preset-env": "^7",
"@types/bcrypt": "^5.0.2",
"@types/better-sqlite3": "^7.6.7",
"@types/cors": "^2.8.13",
Expand All @@ -46,6 +50,8 @@
"@types/uuid": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^5.51.0",
"@typescript-eslint/parser": "^5.51.0",
"babel-jest": "^29.7.0",
"cross-env": "^7.0.3",
"eslint": "^8.33.0",
"eslint-plugin-prettier": "^4.2.1",
"jest": "^29.3.1",
Expand Down
2 changes: 2 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// DOM for URL global in Node 16+
"lib": ["ES2021"],
"allowSyntheticDefaultImports": true,
"allowImportingTsExtensions": true,

Check failure on line 7 in tsconfig.json

View workflow job for this annotation

GitHub Actions / build

Unknown compiler option 'allowImportingTsExtensions'.
"noEmit": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"resolveJsonModule": true,
Expand Down
Loading

0 comments on commit 0f27404

Please sign in to comment.