first commit

This commit is contained in:
2025-04-24 13:11:28 +08:00
commit ff9c54d5e4
5960 changed files with 834111 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
'use strict'
const { expect } = require('chai')
const HTTPStatus = require('http-status')
function expectStatus(err, expected) {
const httpStatus = err.status || err.statusCode
if (httpStatus === undefined) {
throw err
} else {
expect(httpStatus).to.equal(expected)
}
}
async function expectHttpError(promise, expectedStatusCode) {
try {
await promise
} catch (err) {
const statusCode = err.status || err.statusCode
if (statusCode === undefined) {
throw err
} else {
expect(statusCode).to.equal(expectedStatusCode)
return
}
}
expect.fail('expected HTTP request to return with an error response')
}
exports.expectHttpError = expectHttpError
exports.notFound = function (err) {
expectStatus(err, HTTPStatus.NOT_FOUND)
}
exports.unprocessableEntity = function (err) {
expectStatus(err, HTTPStatus.UNPROCESSABLE_ENTITY)
}
exports.conflict = function (err) {
expectStatus(err, HTTPStatus.CONFLICT)
}
exports.unauthorized = function (err) {
expectStatus(err, HTTPStatus.UNAUTHORIZED)
}
exports.forbidden = function (err) {
expectStatus(err, HTTPStatus.FORBIDDEN)
}
exports.requestEntityTooLarge = function (err) {
expectStatus(err, HTTPStatus.REQUEST_ENTITY_TOO_LARGE)
}

View File

@@ -0,0 +1,51 @@
// @ts-check
import config from 'config'
import { startApp } from '../../../../../backup-deletion-app.mjs'
/** @type {import("http").Server} */
let server
/**
* @param {string} pathname
* @return {string}
*/
function testUrl(pathname) {
const url = new URL('http://127.0.0.1')
const addr = server.address()
if (addr && typeof addr === 'object') {
url.port = addr.port.toString()
}
url.pathname = pathname
return url.toString()
}
const basicAuthHeader =
'Basic ' +
Buffer.from(`staging:${config.get('basicHttpAuth.password')}`).toString(
'base64'
)
async function listenOnRandomPort() {
if (server) return // already running
for (let i = 0; i < 10; i++) {
try {
server = await startApp(0)
return
} catch {}
}
server = await startApp(0)
}
after('close server', function (done) {
if (server) {
server.close(done)
} else {
done()
}
})
export default {
testUrl,
basicAuthHeader,
listenOnRandomPort,
}

View File

@@ -0,0 +1,43 @@
// @ts-check
import { startApp } from '../../../../../backup-verifier-app.mjs'
/** @type {import("http").Server} */
let server
/**
* @param {string} pathname
* @return {string}
*/
function testUrl(pathname) {
const url = new URL('http://127.0.0.1')
const addr = server.address()
if (addr && typeof addr === 'object') {
url.port = addr.port.toString()
}
url.pathname = pathname
return url.toString()
}
async function listenOnRandomPort() {
if (server) return // already running
for (let i = 0; i < 10; i++) {
try {
server = await startApp(0)
return
} catch {}
}
server = await startApp(0, false)
}
after('close server', function (done) {
if (server) {
server.close(done)
} else {
done()
}
})
export default {
testUrl,
listenOnRandomPort,
}

View File

@@ -0,0 +1,26 @@
const BPromise = require('bluebird')
const { expect } = require('chai')
const HTTPStatus = require('http-status')
const assert = require('../../../../../storage/lib/assert')
const testServer = require('./test_server')
/**
* Without a provided history id, a new one will get generated.
* The history id could either be a mongo id, or a postgres id.
*
* @param {string} [existingHistoryId]
* @return {Promise<string>}
*/
exports.createEmptyProject = function (existingHistoryId) {
return BPromise.resolve(
testServer.basicAuthClient.apis.Project.initializeProject({
body: { projectId: existingHistoryId },
})
).then(response => {
expect(response.status).to.equal(HTTPStatus.OK)
const { projectId } = response.obj
assert.projectId(projectId, 'bad projectId')
return projectId
})
}

View File

@@ -0,0 +1,133 @@
/**
* @file
* Create a test server. For performance reasons, there is only one test server,
* and it is shared between all of the tests.
*
* This uses the mocha's "root-level hooks" to start and clean up the server.
*/
const BPromise = require('bluebird')
const config = require('config')
const http = require('node:http')
const jwt = require('jsonwebtoken')
const Swagger = require('swagger-client')
const app = require('../../../../../app')
function testUrl(pathname, opts = {}) {
const url = new URL('http://127.0.0.1')
url.port = exports.server.address().port
url.pathname = pathname
if (opts.qs) {
url.searchParams = new URLSearchParams(opts.qs)
}
return url.toString()
}
exports.url = testUrl
function createClient(options) {
// The Swagger client returns native Promises; we use Bluebird promises. Just
// wrapping the client creation is enough in many (but not all) cases to
// get Bluebird into the chain.
return BPromise.resolve(new Swagger(testUrl('/api-docs'), options))
}
function createTokenForProject(projectId, opts = {}) {
const jwtKey = opts.jwtKey || config.get('jwtAuth.key')
const jwtAlgorithm = config.get('jwtAuth.algorithm')
return jwt.sign({ project_id: projectId }, jwtKey, {
algorithm: jwtAlgorithm,
})
}
exports.createTokenForProject = createTokenForProject
function createClientForProject(projectId, opts = {}) {
const token = createTokenForProject(projectId, opts)
return createClient({ authorizations: { jwt: `Bearer ${token}` } })
}
exports.createClientForProject = createClientForProject
function createClientForDownloadZip(projectId) {
const token = createTokenForProject(projectId)
return createClient({ authorizations: { token } })
}
exports.createClientForDownloadZip = createClientForDownloadZip
function createBasicAuthClient() {
return createClient({
authorizations: {
basic: {
username: 'staging',
password: config.get('basicHttpAuth.password'),
},
},
})
}
function createPseudoJwtBasicAuthClient() {
// HACK: The history service will accept HTTP basic auth for any endpoint that
// is expecting a JWT. If / when we fix that, we will need to fix this.
const jwt =
'Basic ' +
Buffer.from(`staging:${config.get('basicHttpAuth.password')}`).toString(
'base64'
)
return createClient({ authorizations: { jwt } })
}
exports.basicAuthHeader =
'Basic ' +
Buffer.from(`staging:${config.get('basicHttpAuth.password')}`).toString(
'base64'
)
function createServer() {
const server = http.createServer(app)
return app.setup().then(() => {
exports.server = server
return server
})
}
function createDefaultUnauthenticatedClient() {
return createClient().then(client => {
exports.client = client
})
}
function createDefaultBasicAuthClient() {
return createBasicAuthClient().then(client => {
exports.basicAuthClient = client
})
}
function createDefaultPseudoJwtBasicAuthClient() {
return createPseudoJwtBasicAuthClient().then(client => {
exports.pseudoJwtBasicAuthClient = client
})
}
before(function () {
function listenOnRandomPort(server) {
const listen = BPromise.promisify(server.listen, { context: server })
return listen(0).catch(err => {
if (err.code !== 'EADDRINUSE' && err.code !== 'EACCES') throw err
return listenOnRandomPort(server)
})
}
return createServer()
.then(listenOnRandomPort)
.then(createDefaultUnauthenticatedClient)
.then(createDefaultBasicAuthClient)
.then(createDefaultPseudoJwtBasicAuthClient)
})
after(function () {
exports.server.close()
})