Skip to content

Commit

Permalink
chore(node,core): add more test types
Browse files Browse the repository at this point in the history
Trying to understand better how these tests and scenarios interact.
  • Loading branch information
boneskull committed Aug 15, 2023
1 parent c71b049 commit a71d561
Show file tree
Hide file tree
Showing 10 changed files with 336 additions and 33 deletions.
10 changes: 10 additions & 0 deletions packages/core/src/generatePolicy.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ const rootSlug = '$root$'

module.exports = { rootSlug, createModuleInspector, getDefaultPaths }

/**
* @todo type this
* @typedef {import('node:events').EventEmitter & {inspectModule: (...args?: any[]) => any, generatePolicy(...args?: any[]) => any} ModuleInspector
*/

/**
* @todo type this
* @param {*} opts
* @returns {ModuleInspector}
*/
function createModuleInspector (opts = {}) {
const moduleIdToModuleRecord = new Map()
// "packageToModules" does not include builtin modules
Expand Down
15 changes: 15 additions & 0 deletions packages/core/src/moduleRecord.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@

/**
* @typedef LavamoatModuleRecordOpts
* @property {string} specifier
* @property {any} file
* @property {string} packageName
* @property {string} content
* @property {Record<string, string>} [importMap]
* @property {any} [ast]
* @property {any} [moduleInitializer]
*/

class LavamoatModuleRecord {
/**
*
* @param {LavamoatModuleRecordOpts} opts
*/
constructor ({
specifier,
file,
Expand Down
53 changes: 44 additions & 9 deletions packages/core/src/parseForPolicy.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,53 @@
// @ts-check

const { createModuleInspector } = require('./generatePolicy')
const { LavamoatModuleRecord } = require('./moduleRecord')
const { eachNodeInTree } = require('./walk')

module.exports = { parseForPolicy }

/**
* @function parseForPolicy
* @param {object} options
* @param {string} options.moduleSpecifier
* @param {function} options.isBuiltin
* @param {function} options.shouldImport
* @param {object} options.policyOverride
* @param {bool} options.includeDebugInfo
* @param {ModuleInspector} options.inspector
* @returns {JSON} policy object
* @callback ImportHookFn
* @param {string} address
* @returns {Promise<LavamoatModuleRecord>}
*/

/**
* @callback IsBuiltinFn
* @returns {boolean}
*/

/**
* @callback ShouldImportFn
* @param {string} childSpecifier
* @param {string} moduleSpecifier
* @returns {boolean}
*/

/**
* @callback ResolveFn
* @param {string} requestedName
* @param {string} parentAddress
* @returns {string|undefined}
*/


/**
* @typedef ParseForPolicyOpts
* @property {string} moduleSpecifier
* @property {ImportHookFn} importHook
* @property {IsBuiltinFn} [isBuiltin]
* @property {ShouldImportFn} [shouldImport]
* @property {ResolveFn} [resolveHook]
* @property {import('../schema').LavaMoatPolicy} [policyOverride]
* @property {boolean} [includeDebugInfo]
* @property {import('./generatePolicy').ModuleInspector} [inspector]
*/

/**
*
* @param {ParseForPolicyOpts} opts
* @returns {Promise<import('../schema').LavaMoatPolicy>} policy object
*/
async function parseForPolicy ({
moduleSpecifier,
Expand Down
28 changes: 17 additions & 11 deletions packages/core/src/walk.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
// @ts-check

// specifier = exact unique module name
// requestedName = what a module asks to import

module.exports = { walk, eachNodeInTree }

/**
* @function walk
* @callback VisitorFn
* @param {import('./moduleRecord').LavamoatModuleRecord} moduleRecord
* @returns {void}
*/

/**
* @param {object} options
* @param {string} options.moduleSpecifier
* @param {function} options.importHook
* @param {function} options.visitorFn
* @param {function} options.shouldImport
* @param {Set<string>} options.visitedSpecifiers
* @param {import('./parseForPolicy').ImportHookFn} options.importHook
* @param {VisitorFn} options.visitorFn
* @param {import('./parseForPolicy').ShouldImportFn} options.shouldImport
* @param {Set<string>} [options.visitedSpecifiers]
*/
async function walk ({
moduleSpecifier,
Expand All @@ -31,13 +38,12 @@ async function walk ({
}

/**
* @function eachNodeInTree
* @param {object} options
* @param {string} options.moduleSpecifier,
* @param {function} options.importHook,
* @param {bool} options.shouldImport,
* @param {Set<string>} options.visitedSpecifiers
* @returns {AsyncIterableIterator<LavamoatModuleRecord>}
* @param {string} options.moduleSpecifier
* @param {import('./parseForPolicy').ImportHookFn} options.importHook
* @param {import('./parseForPolicy').ShouldImportFn} [options.shouldImport]
* @param {Set<string>} [options.visitedSpecifiers]
* @returns {AsyncIterableIterator<import('./moduleRecord').LavamoatModuleRecord>}
*/
// NOTE: i think this is depth first in a way that doesnt take advantage of concurrency
async function * eachNodeInTree ({
Expand Down
4 changes: 3 additions & 1 deletion packages/core/test/scenarios/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const transforms = require('./transforms')
const globalRef = require('./globalRef')
const scuttle = require('./scuttle')

module.exports = { loadScenarios }
/** @type {import('./scenario').ScenarioFactory[]} */
const scenarios = [
...autogen,
...security,
Expand All @@ -30,3 +30,5 @@ async function * loadScenarios () {
yield await scenarioCreator()
}
}

module.exports = { loadScenarios }
127 changes: 127 additions & 0 deletions packages/core/test/scenarios/scenario.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// @ts-check

import type { ExecutionContext } from "ava";
import type { LavaMoatOpts } from "../../../node/src/defaults";
import type { LavaMoatPolicySchema } from "../../schema";

export type ScenarioType = "truthy" | "falsy" | "deepEqual";

export type ScenarioFileType = "builtin" | "js" | "native";

export type JSFilepath = `${string}.js`;

/**
* Scenario file which is _not_ a `.js` file and has had defaults applied
*/
export interface NormalizedScenarioFile extends ScenarioFile {
file: string;
}

/**
* Scenario file which is a `.js` file and has had defaults applied
*/
export interface NormalizedScenarioJSFile extends ScenarioFile {
file: JSFilepath;
specifier: string;
packageName: string;
type: ScenarioFileType;
entry?: true;
importMap: Record<string, string>;
}

/**
* Scenario file as provided by user
*/
export interface ScenarioFile {
/**
* File content
*/
content: string;
/**
* Filepath
*/
file?: string;
specifier?: string;
packageName?: string;
type?: ScenarioFileType;
entry?: true;
importMap?: Record<string, string>;
moduleInitializer?: (
exports: Record<string, any>,
require: (id: string) => any,
module: Record<string, any>
) => void;
}

export type ScenarioSourceFn = () => void;

export interface Scenario<Result = unknown> {
name?: string;
config?: LavaMoatPolicySchema;
configOverride?: LavaMoatPolicySchema;
expectedFailure?: boolean;
expectedResult?: any;
defineEntry?: ScenarioSourceFn;
defineOne?: ScenarioSourceFn;
defineTwo?: ScenarioSourceFn;
defineThree?: ScenarioSourceFn;
testType?: ScenarioType;
expectedFailureMessageRegex?: RegExp;
files?: Record<string, ScenarioFile>;
defaultPolicy?: boolean;
builtin?: Record<string, any>;
context?: Record<string, any>;
opts?: LavaMoatOpts;
dir?: string;
checkPostRun?: ScenarioCheckPostRunFn<Result>;
checkError?: ScenarioCheckErrorFn<Result>;
checkResult?: ScenarioCheckResultFn<Result>;
kernelArgs?: Record<string, any>;
beforeCreateKernel?: (scenario: NormalizedScenario<Result>) => void;
}

export type NormalizedScenario<Result = unknown> = Required<
Pick<
Scenario<Result>,
| "name"
| "checkError"
| "checkPostRun"
| "checkResult"
| "testType"
| "builtin"
| "expectedResult"
| "expectedFailure"
| "expectedFailureMessageRegex"
| "files"
| "config"
| "configOverride"
| "context"
| "opts"
>
> &
Pick<Scenario<Result>, "dir" | "kernelArgs" | "beforeCreateKernel"> & {
entries: string[];
globalThis?: Record<string, any>;
vmContext?: Record<string, any>;
};

export type ScenarioCheckPostRunFn<Result = unknown> = (
t: ExecutionContext,
result: Result | undefined,
err: Error | undefined,
scenario: Scenario<Result>
) => Promise<void>;

export type ScenarioCheckResultFn<Result = unknown> = (
t: ExecutionContext,
result: Result,
scenario: Scenario<Result>
) => Promise<void>;

export type ScenarioCheckErrorFn<Result = unknown> = (
t: ExecutionContext,
err: Error,
scenario: Scenario<Result>
) => Promise<void>;

export type ScenarioFactory = () => Promise<Scenario>;
5 changes: 4 additions & 1 deletion packages/core/test/scenarios/transforms.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
const { createScenarioFromScaffold } = require('../util.js')
const { createScenarioFromScaffold } = require('../util')

module.exports = [
async () => {
const scenario = createScenarioFromScaffold({
name: 'transforms - Ses transforms work',
defineOne: () => {
// @ts-ignore
const two = require('two')
module.exports = two
},
Expand All @@ -23,6 +24,7 @@ module.exports = [
// we still need to ensure this pattern works
name: 'transforms - common pattern "_inheritsLoose" works with TypeError',
defineOne: () => {
// @ts-ignore
const two = require('two')
module.exports = { two }
},
Expand All @@ -43,6 +45,7 @@ module.exports = [
name: 'transforms - common pattern "_inheritsLoose" works across package boundaries',
defineOne: () => {
/* eslint-disable */
// @ts-ignore
const SuperClass = require('two')
function SubClass () {}
_inheritsLoose(SubClass, SuperClass)
Expand Down

0 comments on commit a71d561

Please sign in to comment.