Skip to content
Closed
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ dist
coverage
.build
.test
/.pnpm-test-store
18 changes: 9 additions & 9 deletions packages/plugins/swr/tests/swr.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,13 @@ ${sharedModel}
{
provider: 'postgresql',
pushDb: false,
extraDependencies: [
`${normalizePath(path.join(__dirname, '../dist'))}`,
'react@18.2.0',
'@types/react@18.2.0',
'swr@^2',
],
extraDependencies: {
"react": "^18.2.0",
'swr': '^2.0.0'
},
extraDevDependencies: {
'@types/react': '^18.2.0'
},
compile: true,
}
);
Expand Down Expand Up @@ -94,8 +95,7 @@ ${sharedModel}
`,
{
pushDb: false,
projectDir,
extraDependencies: [`${normalizePath(path.join(__dirname, '../dist'))}`],
projectDir
}
);

Expand All @@ -122,7 +122,7 @@ ${sharedModel}
password String @omit
}
`,
{ pushDb: false, projectDir, extraDependencies: [`${normalizePath(path.join(__dirname, '../dist'))}`] }
{ pushDb: false, projectDir }
)
).rejects.toThrow('already exists and is not a directory');
});
Expand Down
15 changes: 11 additions & 4 deletions packages/schema/tests/generator/prisma-generator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,29 @@ import path from 'path';
import tmp from 'tmp';
import { loadDocument } from '../../src/cli/cli-util';
import { PrismaSchemaGenerator } from '../../src/plugins/prisma/schema-generator';
import { execSync } from '../../src/utils/exec-utils';
import { loadModel } from '../utils';
import { buildPackageJsonContents, initProjectDir } from '../utils';

tmp.setGracefulCleanup();

describe('Prisma generator test', () => {
let origDir: string;

let packageJsonContents: string;

beforeAll(async () => {
packageJsonContents = buildPackageJsonContents({
'prisma': '^5.0.0'
}, {}, false);
});

beforeEach(() => {
origDir = process.cwd();
const r = tmp.dirSync({ unsafeCleanup: true });
console.log(`Project dir: ${r.name}`);
console.log('Project dir: ', r.name);
process.chdir(r.name);

execSync('npm init -y', { stdio: 'ignore' });
execSync('npm install prisma');
initProjectDir(r.name, packageJsonContents);
});

afterEach(() => {
Expand Down
105 changes: 105 additions & 0 deletions packages/schema/tests/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import * as tmp from 'tmp';
import { URI } from 'vscode-uri';
import { createZModelServices } from '../src/language-server/zmodel-module';
import { mergeBaseModels } from '../src/utils/ast-utils';
import { execSync } from 'node:child_process';

/* This file contains narrowly duplicated contents from the testtools package to avoid a cyclic dependency between testtools and schema */

/* Duplicated from testtools model.ts to avoid cyclic dependency */

tmp.setGracefulCleanup();

Expand Down Expand Up @@ -97,3 +102,103 @@ export const errorLike = (msg: string) => ({
message: expect.stringContaining(msg),
},
});


/* Duplicated from testtools schema.ts to avoid cyclic dependency */

export function normalizePath(p: string) {
return p ? p.split(path.sep).join(path.posix.sep) : p;
}

export function getWorkspaceRoot(start: string) {
let curr = normalizePath(start);
while (curr && curr !== '/') {
if (fs.existsSync(path.join(curr, 'pnpm-workspace.yaml'))) {
return curr;
} else {
curr = normalizePath(path.dirname(curr));
}
}
return undefined;
}

/* Duplicated from testtools pnpm-project.ts to avoid cyclic dependency */

export const PNPM_STORE_PATH = path.resolve(__dirname, '../../../.pnpm-test-store');
export const NPM_RC_FILE = '.npmrc';
export const NPM_RC_CONTENTS = `store-dir = ${PNPM_STORE_PATH}`;
export const PACKAGE_JSON_FILE = 'package.json';
export const PACKAGE_JSON_CONTENTS = '{"name":"test-project","version":"1.0.0"}';

export function preparePackageJson(dependencies: {[key: string]: string} = {}, devDependencies: {[key: string]: string} = {}, includeDefaults: boolean = true): string {
const tmpDir = tmp.dirSync({ unsafeCleanup: true }).name;
console.log(`Loading dependencies into store via temp dir ${tmpDir}`);
try {
const packageJsonContents = buildPackageJsonContents(dependencies, devDependencies, includeDefaults);

// I considered doing a `pnpm store add` here instead of a plain install. While that worked, I decided against it in the end because it's a secondary way of processing the dependencies and I didn't see a significant downside to just installing and throwing the local project away right after.
initProjectDir(tmpDir, packageJsonContents, false);

return packageJsonContents;
} finally {
fs.rmSync(tmpDir, {recursive: true, force: true});
}
}

export function buildPackageJsonContents(dependencies: {[key: string]: string} = {}, devDependencies: {[key: string]: string} = {}, includeDefaults: boolean = true): string {
if (includeDefaults) {
dependencies = {
"@prisma/client": "^5.14.0",
"zod": "^3.21.0",
"decimal.js": "^10.4.0",
...dependencies
},
devDependencies = {
"prisma": "^5.14.0",
"typescript": "^5.4.0",
"@types/node": "^20.0.0",
...devDependencies
}
}

const absoluteWorkspacePath = getWorkspaceRoot(__dirname);

return `{
"name":"test-project",
"version":"1.0.0",
"dependencies": {
${Object.entries(dependencies).map(([k, v]) => `"${k}": "${v}"`).join(',\n')}
},
"devDependencies": {
${Object.entries(devDependencies).map(([k, v]) => `"${k}": "${v}"`).join(',\n')}
},
"pnpm": {
"overrides": {
"@zenstackhq/language": "file:${absoluteWorkspacePath}/packages/language/dist",
"@zenstackhq/sdk": "file:${absoluteWorkspacePath}/packages/sdk/dist",
"@zenstackhq/runtime": "file:${absoluteWorkspacePath}/packages/runtime/dist"
}
}
}`;
}

export function initProjectDir(projectDir: string, packageJsonContents: string, offline = true) {
try {
if (!fs.existsSync(projectDir)) {
fs.mkdirSync(projectDir, { recursive: true });
}
fs.writeFileSync(path.join(projectDir, PACKAGE_JSON_FILE), packageJsonContents, { flag: 'w+' });
fs.writeFileSync(path.join(projectDir, NPM_RC_FILE), NPM_RC_CONTENTS, { flag: 'w+' });
} catch (e) {
console.error(`Failed to set up project dir in ${projectDir}`);
throw e;
}

try {
execSync(`pnpm install ${offline ? '--prefer-offline ' : ''}--ignore-workspace`, {cwd: projectDir, stdio: 'ignore'});
} catch (e) {
console.error(`Failed to initialize project dependencies in ${projectDir}${offline ? '(offline mode)' : '(online mode)'}`);
throw e;
}
}

1 change: 1 addition & 0 deletions packages/testtools/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './db';
export * from './model';
export * from './schema';
export * from './pnpm-project';
85 changes: 85 additions & 0 deletions packages/testtools/src/pnpm-project.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { execSync } from 'node:child_process';
import * as fs from 'node:fs';
import * as path from 'node:path';
import tmp from 'tmp';
import { getWorkspaceRoot } from './schema';

export const PNPM_STORE_PATH = path.resolve(__dirname, '../../../.pnpm-test-store');
export const NPM_RC_FILE = '.npmrc';
export const NPM_RC_CONTENTS = `store-dir = ${PNPM_STORE_PATH}`;
export const PACKAGE_JSON_FILE = 'package.json';
export const PACKAGE_JSON_CONTENTS = '{"name":"test-project","version":"1.0.0"}';

tmp.setGracefulCleanup();

export function preparePackageJson(dependencies: {[key: string]: string} = {}, devDependencies: {[key: string]: string} = {}, includeDefaults: boolean = true): string {
const tmpDir = tmp.dirSync({ unsafeCleanup: true }).name;
console.log(`Loading dependencies into store via temp dir ${tmpDir}`);
try {
const packageJsonContents = buildPackageJsonContents(dependencies, devDependencies, includeDefaults);

// I considered doing a `pnpm store add` here instead of a plain install. While that worked, I decided against it in the end because it's a secondary way of processing the dependencies and I didn't see a significant downside to just installing and throwing the local project away right after.
initProjectDir(tmpDir, packageJsonContents, false);

return packageJsonContents;
} finally {
fs.rmSync(tmpDir, {recursive: true, force: true});
}
}

export function buildPackageJsonContents(dependencies: {[key: string]: string} = {}, devDependencies: {[key: string]: string} = {}, includeDefaults: boolean = true): string {
if (includeDefaults) {
dependencies = {
"@prisma/client": "^5.14.0",
"zod": "^3.21.0",
"decimal.js": "^10.4.0",
...dependencies
},
devDependencies = {
"prisma": "^5.14.0",
"typescript": "^5.4.0",
"@types/node": "^20.0.0",
...devDependencies
}
}

const absoluteWorkspacePath = getWorkspaceRoot(__dirname);

return `{
"name":"test-project",
"version":"1.0.0",
"dependencies": {
${Object.entries(dependencies).map(([k, v]) => `"${k}": "${v}"`).join(',\n')}
},
"devDependencies": {
${Object.entries(devDependencies).map(([k, v]) => `"${k}": "${v}"`).join(',\n')}
},
"pnpm": {
"overrides": {
"@zenstackhq/language": "file:${absoluteWorkspacePath}/packages/language/dist",
"@zenstackhq/sdk": "file:${absoluteWorkspacePath}/packages/sdk/dist",
"@zenstackhq/runtime": "file:${absoluteWorkspacePath}/packages/runtime/dist"
}
}
}`;
}

export function initProjectDir(projectDir: string, packageJsonContents: string, offline = true) {
try {
if (!fs.existsSync(projectDir)) {
fs.mkdirSync(projectDir, { recursive: true });
}
fs.writeFileSync(path.join(projectDir, PACKAGE_JSON_FILE), packageJsonContents, { flag: 'w+' });
fs.writeFileSync(path.join(projectDir, NPM_RC_FILE), NPM_RC_CONTENTS, { flag: 'w+' });
} catch (e) {
console.error(`Failed to set up project dir in ${projectDir}`);
throw e;
}

try {
execSync(`pnpm install ${offline ? '--prefer-offline ' : ''}--ignore-workspace`, {cwd: projectDir, stdio: 'ignore'});
} catch (e) {
console.error(`Failed to initialize project dependencies in ${projectDir}${offline ? '(offline mode)' : '(online mode)'}`);
throw e;
}
}
Loading