Skip to content

Commit

Permalink
chore(core): expose types from test/util and scenario definition
Browse files Browse the repository at this point in the history
In order for other packages to consume the types from `test/util.js` (and the `Scenario` type), `lavamoat-core` needs to generate `.d.ts` files.  To do this, I:

1. Moved `test/scenarios/scenario.d.ts` (which is not typechecked!) to `test/scenario.ts` (which is)
2. Changed `test/tsconfig.json` to output declarations (instead of nothing) for both `test/util.js` and `test/scenario.ts`
3. Update `test/util.js` with the new location of the `scenario` module (and fix a type problem I found)
  • Loading branch information
boneskull committed Mar 20, 2024
1 parent 1459ad5 commit 137a60d
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 54 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// @ts-check

import type { ExecutionContext } from 'ava'
import type { LavaMoatOpts } from '../../../node/src/defaults'
import type { LavaMoatPolicySchema } from '../../schema'
import { ModuleInitializer } from '../src/moduleRecord'
import type { LavaMoatOpts } from '../src/options'
import { LavaMoatPolicy, LavaMoatPolicyOverrides } from '../src/schema'

export type ScenarioType = 'truthy' | 'falsy' | 'deepEqual'

Expand Down Expand Up @@ -31,11 +30,7 @@ export interface NormalizedScenarioJSFile extends ScenarioFile {

export interface NormalizedBuiltin extends NormalizedScenarioJSFile {
specifier: string
moduleInitializer: (
exports: Record<string, any>,
require: (id: string) => any,
module: Record<string, any>
) => void
moduleInitializer: ModuleInitializer
}
/**
* Scenario file as provided by user
Expand All @@ -54,21 +49,17 @@ export interface ScenarioFile {
type?: ScenarioFileType
entry?: boolean
importMap?: Record<string, string>
moduleInitializer?: (
exports: Record<string, any>,
require: (id: string) => any,
module: Record<string, any>
) => void
moduleInitializer?: ModuleInitializer
}

export type ScenarioSourceFn = () => void

export interface Scenario<Result = unknown> {
name?: string
config?: LavaMoatPolicySchema
configOverride?: LavaMoatPolicySchema
config?: LavaMoatPolicy
configOverride?: LavaMoatPolicyOverrides
expectedFailure?: boolean
expectedResult?: any
expectedResult?: Result
defineEntry?: ScenarioSourceFn
defineOne?: ScenarioSourceFn
defineTwo?: ScenarioSourceFn
Expand All @@ -77,14 +68,14 @@ export interface Scenario<Result = unknown> {
expectedFailureMessageRegex?: RegExp
files?: Record<string, ScenarioFile>
defaultPolicy?: boolean
builtin?: Record<string, any>
context?: Record<string, any>
builtin?: Record<string, unknown>
context?: Record<string, unknown>
opts?: LavaMoatOpts
dir?: string
checkPostRun?: ScenarioCheckPostRunFn<Result>
checkError?: ScenarioCheckErrorFn<Result>
checkResult?: ScenarioCheckResultFn<Result>
kernelArgs?: Record<string, any>
kernelArgs?: Record<string, unknown>
beforeCreateKernel?: (scenario: NormalizedScenario<Result>) => void
}

Expand All @@ -109,8 +100,8 @@ export type NormalizedScenario<Result = unknown> = Required<
> &
Pick<Scenario<Result>, 'dir' | 'kernelArgs' | 'beforeCreateKernel'> & {
entries: string[]
globalThis?: Record<string, any>
vmContext?: Record<string, any>
globalThis?: Record<string, unknown>
vmContext?: Record<string, unknown>
}

export type ScenarioCheckPostRunFn<Result = unknown> = (
Expand Down
8 changes: 5 additions & 3 deletions packages/core/test/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{
"extends": "../../../.config/tsconfig.test.json",
// not extending the test config b/c we must output some types
"extends": "../../../.config/tsconfig.src.json",
"compilerOptions": {
"checkJs": false,
"rootDir": "."
"rootDir": "..",
"outDir": "../types"
},
"include": ["**/*"],
"include": ["**/*.js", "**/*.ts"],
"references": [
{
"path": "../src/tsconfig.json"
Expand Down
57 changes: 28 additions & 29 deletions packages/core/test/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ module.exports = {

/**
* @typedef {Partial<import('../src/parseForPolicy').ParseForPolicyOpts> & {
* files: import('./scenarios/scenario').NormalizedScenarioJSFile[]
* files: import('./scenario').NormalizedScenarioJSFile[]
* }} GeneratePolicyFromFilesOpts
*/

Expand All @@ -41,17 +41,17 @@ module.exports = {
async function generatePolicyFromFiles({ files, ...opts }) {
const config = await parseForPolicy({
moduleSpecifier:
/** @type {import('./scenarios/scenario').NormalizedScenarioJSFile} */ (
/** @type {import('./scenario').NormalizedScenarioJSFile} */ (
files.find((file) => file.entry)
).specifier,
resolveHook: (requestedName, parentAddress) => {
return /** @type {import('./scenarios/scenario').NormalizedScenarioJSFile} */ (
return /** @type {import('./scenario').NormalizedScenarioJSFile} */ (
files.find((file) => file.specifier === parentAddress)
).importMap[requestedName]
},
importHook: async (address) => {
return new LavamoatModuleRecord(
/** @type {import('./scenarios/scenario').NormalizedBuiltin} */ (
/** @type {import('./scenario').NormalizedBuiltin} */ (
files.find((file) => file.specifier === address)
)
)
Expand All @@ -69,8 +69,8 @@ async function generatePolicyFromFiles({ files, ...opts }) {
* running.
*
* @template [Result=unknown] Default is `unknown`
* @param {import('./scenarios/scenario').Scenario<Result>} scenario
* @returns {import('./scenarios/scenario').NormalizedScenario<Result>}
* @param {import('./scenario').Scenario} scenario
* @returns {import('./scenario').NormalizedScenario<{ value: string }>}
*/
function createScenarioFromScaffold({
name = 'template scenario',
Expand All @@ -80,12 +80,12 @@ function createScenarioFromScaffold({
testType = 'deepEqual',
checkPostRun = async (t, result, err, scenario) => {
if (err) {
await /** @type {import('./scenarios/scenario').ScenarioCheckErrorFn<Result>} */ (
await /** @type {import('./scenario').ScenarioCheckErrorFn<Result>} */ (
scenario.checkError
)(t, err, scenario)
} else {
// assumes `result` is not undefined
await /** @type {import('./scenarios/scenario').ScenarioCheckResultFn<Result>} */ (
await /** @type {import('./scenario').ScenarioCheckResultFn<Result>} */ (
scenario.checkResult
)(t, /** @type {Result} */ (result), scenario)
}
Expand Down Expand Up @@ -298,8 +298,11 @@ function createScenarioFromScaffold({
expectedFailureMessageRegex,
entries: ['entry.js'],
files: _files,
config: _config,
configOverride: _configOverride,
config: /** @type {import('../src/schema').LavaMoatPolicy} */ (_config),
configOverride:
/** @type {import('../src/schema').LavaMoatPolicyOverrides} */ (
_configOverride
),
context,
opts,
dir,
Expand Down Expand Up @@ -342,7 +345,7 @@ function createHookedConsole() {
*
* @template [Result=unknown] Default is `unknown`
* @typedef PlatformRunScenarioOpts
* @property {import('./scenarios/scenario').NormalizedScenario<Result>} scenario
* @property {import('./scenario').NormalizedScenario<Result>} scenario
* @property {boolean} [runWithPrecompiledModules]
* @property {(...args: any[]) => void} [log]
*/
Expand Down Expand Up @@ -446,9 +449,8 @@ async function runScenario({ scenario, runWithPrecompiledModules = false }) {
*/
getRelativeModuleId: (id, relative) => {
return (
/** @type {import('./scenarios/scenario').NormalizedScenarioJSFile} */ (
files[id]
).importMap[relative] || relative
/** @type {import('./scenario').NormalizedScenarioJSFile} */ (files[id])
.importMap[relative] || relative
)
},
prepareModuleInitializerArgs,
Expand Down Expand Up @@ -479,8 +481,8 @@ async function runScenario({ scenario, runWithPrecompiledModules = false }) {
* @param {Object} options - The options for preparing the scenario.
* @param {FsPromiseApi} [options.fs] - The file system module to use (default:
* `node:fs/promises`).
* @param {import('./scenarios/scenario').Scenario} options.scenario - The
* scenario object containing the files to write.
* @param {import('./scenario').Scenario} options.scenario - The scenario object
* containing the files to write.
* @param {string} [options.policyName='policies'] - The name of the policy
* directory (default: 'policies'). Default is `'policies'`
* @param {string} [options.projectDir] - The project directory path.
Expand Down Expand Up @@ -522,9 +524,7 @@ async function prepareScenarioOnDisk({
filesToWrite.map(async (file) => {
const fullPath = path.join(
/** @type {string} */ (projectDir),
/** @type {import('./scenarios/scenario').NormalizedScenarioJSFile} */ (
file
).file
/** @type {import('./scenario').NormalizedScenarioJSFile} */ (file).file
)
const dirname = path.dirname(fullPath)
await fs.mkdir(dirname, { recursive: true })
Expand All @@ -538,7 +538,7 @@ async function prepareScenarioOnDisk({
}

/**
* @param {Record<string, import('./scenarios/scenario').ScenarioFile>} files
* @param {Record<string, import('./scenario').ScenarioFile>} files
* @returns
*/
function fillInFileDetails(files) {
Expand Down Expand Up @@ -566,7 +566,7 @@ function moduleDataForBuiltin(builtinObj, name) {
file: name,
package: name,
type: 'builtin',
/** @type {import('./scenarios/scenario').ScenarioFile['moduleInitializer']} */
/** @type {import('./scenario').ScenarioFile['moduleInitializer']} */
moduleInitializer: (_, _2, module) => {
module.exports = builtinObj[name]
},
Expand Down Expand Up @@ -647,7 +647,7 @@ function evaluateWithSourceUrl(filename, content, context) {
* @returns {Promise<import('../src/schema').LavaMoatPolicy>}
*/
async function createConfigForTest(testFn, opts = {}) {
/** @type {import('./scenarios/scenario').NormalizedScenarioJSFile[]} */
/** @type {import('./scenario').NormalizedScenarioJSFile[]} */
const files = [
{
type: 'js',
Expand Down Expand Up @@ -676,21 +676,20 @@ async function createConfigForTest(testFn, opts = {}) {

/**
* @param {object} opts
* @param {import('./scenarios/scenario').Scenario} opts.scenario
* @param {import('./scenario').Scenario} opts.scenario
* @param {Partial<GeneratePolicyFromFilesOpts>} [opts.opts]
*/
async function autoConfigForScenario({ scenario, opts = {} }) {
const files =
/** @type {import('./scenarios/scenario').NormalizedScenarioJSFile[]} */ (
Object.values(scenario.files ?? {})
)
const files = /** @type {import('./scenario').NormalizedScenarioJSFile[]} */ (
Object.values(scenario.files ?? {})
)
const policy = await generatePolicyFromFiles({ files, ...opts })
scenario.config = policy
}

/**
* @param {object} opts
* @param {import('./scenarios/scenario').NormalizedScenario} opts.scenario
* @param {import('./scenario').NormalizedScenario} opts.scenario
*/
function convertOptsToArgs({ scenario }) {
const { entries } = scenario
Expand All @@ -712,7 +711,7 @@ function functionToString(func) {
/**
* @template [Result=unknown] Default is `unknown`
* @param {import('ava').ExecutionContext} t
* @param {import('./scenarios/scenario').NormalizedScenario<Result>} scenario
* @param {import('./scenario').NormalizedScenario<Result>} scenario
* @param {PlatformRunScenario<Result>} platformRunScenario
* @returns {Promise<Result | undefined>}
*/
Expand Down

0 comments on commit 137a60d

Please sign in to comment.