[tests] Use AL2 build container for relevant e2e tests (#11329)

* Forces the AL2 build container image for fixtures that depend on it,
via `engines.node` in package.json for most cases.
* The `testDeployment()` function was updated to send
`projectSettings.nodeVersion` in the POST body, to mimic the behavior in
CLI.
* For Go, Ruby, and Python tests, the `projectSettings.nodeVersion`
property is set "globally" in the Jest setup file, so that individual
fixtures didn't need to be adjusted.
This commit is contained in:
Nathan Rajlich
2024-03-27 12:19:30 -07:00
committed by GitHub
parent 6487d091df
commit c82a55c460
54 changed files with 207 additions and 57 deletions

View File

@@ -0,0 +1,2 @@
---
---

View File

@@ -4,7 +4,7 @@ import { lstatSync, readdirSync } from 'fs';
export async function deployExample(filename: string) {
const { testDeployment } = require('../../test/lib/deployment/test-deployment.js');
const example = basename(filename).replace(/\.test\.ts$/, '');
await testDeployment(join(filename, '..', '..', '..', example));
await testDeployment(join(process.cwd(), example));
}
export function getExamples() {

View File

@@ -9,6 +9,7 @@
},
"devDependencies": {
"@types/jest": "27.4.1",
"@vercel/build-utils": "7.9.1",
"@vercel/frameworks": "3.0.0"
},
"version": null

View File

@@ -3,5 +3,8 @@
"version": "1.0.0",
"scripts": {
"build": "mkdir -p public && (printf \"npm version: \" && npm -v) > public/index.txt"
},
"engines": {
"node": "16.x"
}
}

View File

@@ -5,5 +5,8 @@
},
"dependencies": {
"once": "^1.4.0"
},
"engines": {
"node": "16.x"
}
}

View File

@@ -6,5 +6,8 @@
"dependencies": {
"exeggcute": "^1.0.0",
"once": "^1.4.0"
},
"engines": {
"node": "16.x"
}
}

View File

@@ -5,5 +5,8 @@
},
"dependencies": {
"sharp": "0.33.2"
},
"engines": {
"node": "16.x"
}
}

View File

@@ -0,0 +1,6 @@
{
"private": true,
"engines": {
"node": "16.x"
}
}

View File

@@ -0,0 +1,6 @@
{
"private": true,
"engines": {
"node": "16.x"
}
}

View File

@@ -0,0 +1,6 @@
{
"private": true,
"engines": {
"node": "16.x"
}
}

View File

@@ -0,0 +1,6 @@
{
"private": true,
"engines": {
"node": "16.x"
}
}

View File

@@ -0,0 +1,6 @@
{
"private": true,
"engines": {
"node": "16.x"
}
}

View File

@@ -119,7 +119,6 @@ test(
await testPath(200, `/api/date`, new RegExp(`Current date is ${year}`));
await testPath(200, `/api/date.py`, new RegExp(`Current date is ${year}`));
await testPath(200, `/api/headers`, (body: any, res: any) => {
// @ts-ignore
const { host } = new URL(res.url);
expect(body).toBe(host);
});
@@ -198,7 +197,6 @@ test(
);
await testPath(200, `/api/dump`, (body: any, res: any, isDev: any) => {
// @ts-ignore
const { host } = new URL(res.url);
const { env, headers } = JSON.parse(body);

View File

@@ -7,5 +7,8 @@
"dependencies": {
"exeggcute": "^1.0.0",
"once": "^1.4.0"
},
"engines": {
"node": "18.x"
}
}

View File

@@ -348,6 +348,9 @@ module.exports = async function prepare(session, binaryPath, tmpFixturesDir) {
},
'lambda-with-php-runtime': {
'api/test.php': `<?php echo 'Hello from PHP'; ?>`,
'package.json': JSON.stringify({
engines: { node: '18.x' },
}),
'vercel.json': JSON.stringify({
functions: {
'api/**/*.php': {

View File

@@ -11,10 +11,15 @@ const fixturesPath = path.resolve(__dirname, 'fixtures');
// eslint-disable-next-line no-restricted-syntax
for (const fixture of fs.readdirSync(fixturesPath)) {
// Go endpoints currently require the AL2 build image
const projectSettings = {
nodeVersion: '18.x',
};
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(
testDeployment(path.join(fixturesPath, fixture))
testDeployment(path.join(fixturesPath, fixture), { projectSettings })
).resolves.toBeDefined();
});
}

View File

@@ -1,13 +0,0 @@
const { strictEqual } = require('assert');
async function test3({ deploymentUrl, fetch, randomness }) {
const bodyMustBe = `${randomness}:content-length`;
const resp = await fetch(`https://${deploymentUrl}/test3.js`);
strictEqual(resp.status, 401);
strictEqual(await resp.text(), bodyMustBe);
strictEqual(resp.headers.get('content-length'), String(bodyMustBe.length));
}
module.exports = async ({ deploymentUrl, fetch, randomness }) => {
await test3({ deploymentUrl, fetch, randomness });
};

View File

@@ -1,4 +0,0 @@
module.exports = (_, resp) => {
resp.writeHead(401);
resp.end(`${process.env.RANDOMNESS_ENV_VAR}:content-length`);
};

View File

@@ -1,8 +0,0 @@
{
"version": 2,
"builds": [
{ "src": "test1.js", "use": "@vercel/node" },
{ "src": "test2.js", "use": "@vercel/node" },
{ "src": "test3.js", "use": "@vercel/node" }
]
}

View File

@@ -1,5 +1,5 @@
{
"engines": {
"node": "16.x"
"node": "20.x"
}
}

View File

@@ -0,0 +1,8 @@
{
"probes": [
{ "path": "/empty", "mustContain": "RANDOMNESS_PLACEHOLDER:20" },
{ "path": "/greater", "mustContain": "RANDOMNESS_PLACEHOLDER:20" },
{ "path": "/major", "mustContain": "RANDOMNESS_PLACEHOLDER:20" },
{ "path": "/range", "mustContain": "RANDOMNESS_PLACEHOLDER:20" }
]
}

View File

@@ -1,5 +1,5 @@
{
"engines": {
"node": "16.0.0 - 16.99.99"
"node": "20.0.0 - 20.99.99"
}
}

View File

@@ -5,11 +5,5 @@
"src": "**/*.js",
"use": "@vercel/node"
}
],
"probes": [
{ "path": "/empty", "mustContain": "RANDOMNESS_PLACEHOLDER:18" },
{ "path": "/greater", "mustContain": "RANDOMNESS_PLACEHOLDER:18" },
{ "path": "/major", "mustContain": "RANDOMNESS_PLACEHOLDER:16" },
{ "path": "/range", "mustContain": "RANDOMNESS_PLACEHOLDER:16" }
]
}

View File

@@ -1,11 +1,16 @@
{
"version": 2,
"builds": [
{ "src": "api/yes.py", "use": "@vercel/python" },
{
"src": "api/yes.py",
"use": "@vercel/python"
},
{
"src": "api/no.py",
"use": "@vercel/python",
"config": { "excludeFiles": "ignoreme/**" }
"config": {
"excludeFiles": "ignoreme/**"
}
}
]
}

View File

@@ -28,12 +28,19 @@ module.exports = function setupTests(groupIndex) {
// eslint-disable-next-line no-restricted-syntax
for (const fixture of chunkedFixtures) {
// Python endpoints currently require the AL2 build image
const projectSettings = {
nodeVersion: '18.x',
};
const errMsg = testsThatFailToBuild.get(fixture);
if (errMsg) {
// eslint-disable-next-line no-loop-func
it(`should fail to build ${fixture}`, async () => {
try {
await testDeployment(path.join(fixturesPath, fixture));
await testDeployment(path.join(fixturesPath, fixture), {
projectSettings,
});
} catch (err) {
expect(err).toBeTruthy();
expect(err.deployment).toBeTruthy();
@@ -45,7 +52,9 @@ module.exports = function setupTests(groupIndex) {
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(
testDeployment(path.join(fixturesPath, fixture))
testDeployment(path.join(fixturesPath, fixture), {
projectSettings,
})
).resolves.toBeDefined();
});
}

View File

@@ -23,6 +23,6 @@
"typescript": "^4.6.4"
},
"engines": {
"node": ">=14"
"node": "18.x"
}
}

View File

@@ -25,6 +25,6 @@
"typescript": "^4.6.4"
},
"engines": {
"node": ">=14"
"node": "18.x"
}
}

View File

@@ -22,6 +22,6 @@
"typescript": "^4.6.4"
},
"engines": {
"node": ">=14"
"node": "18.x"
}
}

View File

@@ -23,6 +23,6 @@
"typescript": "^4.6.4"
},
"engines": {
"node": ">=14"
"node": "18.x"
}
}

View File

@@ -13,5 +13,8 @@
},
"devDependencies": {
"@remix-run/dev": "^1.14.0"
},
"engines": {
"node": "18.x"
}
}

View File

@@ -14,5 +14,8 @@
"devDependencies": {
"@remix-run/dev": "^1.14.0",
"@remix-run/serve": "^1.14.0"
},
"engines": {
"node": "18.x"
}
}

View File

@@ -24,6 +24,6 @@
"typescript": "^4.8.4"
},
"engines": {
"node": ">=14"
"node": "18.x"
}
}

View File

@@ -24,6 +24,6 @@
"typescript": "^4.8.4"
},
"engines": {
"node": ">=14"
"node": "18.x"
}
}

View File

@@ -24,6 +24,6 @@
"typescript": "^4.8.4"
},
"engines": {
"node": ">=14"
"node": "18.x"
}
}

View File

@@ -26,12 +26,19 @@ for (const fixture of fs.readdirSync(fixturesPath)) {
continue;
}
// Ruby endpoints currently require the AL2 build image
const projectSettings = {
nodeVersion: '18.x',
};
const errMsg = testsThatFailToBuild.get(fixture);
if (errMsg) {
// eslint-disable-next-line no-loop-func
it(`should fail to build ${fixture}`, async () => {
try {
await testDeployment(path.join(fixturesPath, fixture));
await testDeployment(path.join(fixturesPath, fixture), {
projectSettings,
});
} catch (err) {
expect(err).toBeTruthy();
expect(err.deployment).toBeTruthy();
@@ -43,7 +50,7 @@ for (const fixture of fs.readdirSync(fixturesPath)) {
// eslint-disable-next-line no-loop-func
it(`should build ${fixture}`, async () => {
await expect(
testDeployment(path.join(fixturesPath, fixture))
testDeployment(path.join(fixturesPath, fixture), { projectSettings })
).resolves.toBeDefined();
});
}

View File

@@ -1,5 +1,8 @@
{
"dependencies": {
"cowsay": "^1.3.1"
},
"engines": {
"node": "16.x"
}
}

View File

@@ -1,5 +1,8 @@
{
"dependencies": {
"yodasay": "^1.1.6"
},
"engines": {
"node": "18.x"
}
}

View File

@@ -2,14 +2,14 @@
"name": "12-create-react-app",
"version": "0.1.0",
"private": true,
"engines": {
"node": "16.x"
},
"dependencies": {
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-scripts": "^3.0.1"
},
"engines": {
"node": "16.x"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",

View File

@@ -2,5 +2,8 @@
"private": true,
"scripts": {
"build": "./build.sh"
},
"engines": {
"node": "18.x"
}
}

View File

@@ -2,5 +2,8 @@
"private": true,
"scripts": {
"build": "pelican && mv output public"
},
"engines": {
"node": "18.x"
}
}

View File

@@ -2,5 +2,8 @@
"private": true,
"scripts": {
"build": "mkdocs build && mv site public"
},
"engines": {
"node": "18.x"
}
}

View File

@@ -10,5 +10,8 @@
}
}
],
"probes": [{ "path": "/", "mustContain": "hello world" }]
"probes": [{ "path": "/", "mustContain": "hello world" }],
"projectSettings": {
"nodeVersion": "16.x"
}
}

View File

@@ -10,5 +10,8 @@
"outputDirectory": "custom-output"
}
}
]
],
"projectSettings": {
"nodeVersion": "16.x"
}
}

View File

@@ -9,5 +9,8 @@
"outputDirectory": "custom-output"
}
}
]
],
"projectSettings": {
"nodeVersion": "16.x"
}
}

View File

@@ -5,5 +5,8 @@
},
"dependencies": {
"puppeteer": "20.8.2"
},
"engines": {
"node": "18.x"
}
}

View File

@@ -0,0 +1,5 @@
{
"engines": {
"node": "18.x"
}
}

View File

@@ -2,5 +2,8 @@
"private": true,
"scripts": {
"build": "hugo"
},
"engines": {
"node": "18.x"
}
}

View File

@@ -0,0 +1,5 @@
{
"engines": {
"node": "18.x"
}
}

View File

@@ -0,0 +1,5 @@
{
"engines": {
"node": "18.x"
}
}

View File

@@ -0,0 +1,5 @@
{
"engines": {
"node": "18.x"
}
}

View File

@@ -2,5 +2,8 @@
"private": true,
"scripts": {
"build": "zola build"
},
"engines": {
"node": "18.x"
}
}

3
pnpm-lock.yaml generated
View File

@@ -131,6 +131,9 @@ importers:
'@types/jest':
specifier: 27.4.1
version: 27.4.1
'@vercel/build-utils':
specifier: 7.9.1
version: link:../packages/build-utils
'@vercel/frameworks':
specifier: 3.0.0
version: link:../packages/frameworks

View File

@@ -45,6 +45,10 @@ async function nowDeploy(projectName, bodies, randomness, uploadNowJson, opts) {
files,
meta: {},
...nowJson,
projectSettings: {
...nowJson.projectSettings,
...opts.projectSettings,
},
env: { ...nowJson.env, RANDOMNESS_ENV_VAR: randomness },
build: {
env: {

View File

@@ -8,6 +8,10 @@ const { spawn } = require('child_process');
const fetch = require('./fetch-retry.js');
const { nowDeploy, fileModeSymbol, fetchWithAuth } = require('./now-deploy.js');
const { logWithinTest } = require('./log');
const {
scanParentDirs,
getSupportedNodeVersion,
} = require('@vercel/build-utils');
async function packAndDeploy(builderPath, shouldUnlink = true) {
await spawnAsync('npm', ['--loglevel', 'warn', 'pack'], {
@@ -304,7 +308,7 @@ async function runProbe(probe, deploymentId, deploymentUrl, ctx) {
assert(hadTest, 'probe must have a test condition');
}
async function testDeployment(fixturePath, opts) {
async function testDeployment(fixturePath, opts = {}) {
const projectName = path
.basename(fixturePath)
.toLowerCase()
@@ -348,6 +352,34 @@ async function testDeployment(fixturePath, opts) {
const uploadNowJson = nowJson.uploadNowJson;
delete nowJson.uploadNowJson;
// Set `projectSettings.nodeVersion` based on the "engines.node" field of
// the `package.json`. This ensures the correct build-container version is used.
let rootDirectory = path.join(
fixturePath,
nowJson.builds?.length
? path.dirname(nowJson.builds[0].src)
: nowJson.projectSettings?.rootDirectory ?? ''
);
const { packageJson } = await scanParentDirs(
rootDirectory,
true,
fixturePath
);
let nodeVersion;
if (packageJson?.engines?.node) {
try {
const { range } = await getSupportedNodeVersion(packageJson.engines.node);
nodeVersion = range;
} catch (err) {
console.error(err);
}
}
if (nodeVersion) {
if (!opts.projectSettings) opts.projectSettings = {};
opts.projectSettings.nodeVersion = nodeVersion;
}
const probePath = path.resolve(fixturePath, 'probe.js');
let probes = [];
if ('probes' in nowJson) {