@sdeverywhere/plugin-check 0.3.35 → 0.3.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -22,7 +22,172 @@ yarn add -D @sdeverywhere/plugin-check
22
22
 
23
23
  ## Usage
24
24
 
25
- _TODO: This section needs to be fleshed out. In the meantime, you can refer to the API documentation below for the options used to configure this plugin._
25
+ _Note:_ If you followed the "Quick Start" instructions above and/or are using one of the standard project templates provided by SDEverywhere, the `sde.config.js` file should already be set up to use `plugin-check`.
26
+ Reading these instructions can still be helpful if you are setting up a project manually or want to understand how `plugin-check` can be integrated into your project.
27
+
28
+ ### Why use this plugin?
29
+
30
+ Each time your model is built, this plugin:
31
+
32
+ 1. Generates a _bundle_ (`check-bundle.js`), which packages up your generated model together with metadata about its inputs, outputs, and graphs.
33
+ 2. Runs **check tests**, which assert that a single version of your model behaves as expected (for example, that a stock never goes negative).
34
+ 3. Runs **comparison tests**, which run two different versions ("bundles") of your model side by side and flag any differences. This step is skipped if no `baseline` bundle is configured.
35
+ 4. Builds an interactive **model-check report** that summarizes the results.
36
+
37
+ In development mode (`sde dev`), the report is served from a local dev server and re-runs automatically as you edit your model and tests.
38
+ In production mode (`sde bundle`), the report is written to disk, and the build fails if any check test fails.
39
+
40
+ ### Steps
41
+
42
+ 1. Add `@sdeverywhere/plugin-check` as a project "dev" dependency:
43
+
44
+ ```sh
45
+ cd your-model-project
46
+ npm install --save-dev @sdeverywhere/plugin-check
47
+ ```
48
+
49
+ 2. Update your `sde.config.js` file to use `checkPlugin`. This plugin runs as a `postBuild` step, so it should generally appear near the end of the `plugins` array (but before `deployPlugin`, if you use it):
50
+
51
+ ```js
52
+ import { checkPlugin } from '@sdeverywhere/plugin-check'
53
+
54
+ export async function config() {
55
+ return {
56
+ modelFiles: ['model/example.mdl'],
57
+
58
+ // Re-run checks when the model or the test definitions change
59
+ watchPaths: ['model/**'],
60
+
61
+ // ...
62
+
63
+ plugins: [
64
+ // ...
65
+
66
+ // Build or serve the model-check report
67
+ checkPlugin({
68
+ // There are no required options; see below for optional configuration
69
+ })
70
+ ]
71
+ }
72
+ }
73
+ ```
74
+
75
+ 3. Write check tests in one or more YAML files (see "Defining tests" below).
76
+
77
+ 4. Run `sde dev` to work on your model with the report open in a browser, or `sde bundle` to build the report as part of a production build.
78
+
79
+ ### Defining tests
80
+
81
+ By default, the plugin discovers test definitions by scanning your project directory for YAML files:
82
+
83
+ - Check tests: files matching `**/checks/*.yaml`.
84
+ - Comparison tests: files matching `**/comparisons/*.yaml`.
85
+
86
+ A common layout is to keep them next to the model:
87
+
88
+ ```
89
+ model/
90
+ ├── example.mdl
91
+ ├── checks/
92
+ | └── checks.yaml
93
+ └── comparisons/
94
+ └── comparisons.yaml
95
+ ```
96
+
97
+ A simple `checks.yaml` file looks like this:
98
+
99
+ ```yaml
100
+ # yaml-language-server: $schema=../../node_modules/@sdeverywhere/plugin-check/node_modules/@sdeverywhere/check-core/schema/check.schema.json
101
+
102
+ - describe: Total inventory
103
+ tests:
104
+ - it: should be in the range [1000,1300] for all input scenarios
105
+ scenarios:
106
+ - preset: matrix
107
+ datasets:
108
+ - name: Total inventory
109
+ predicates:
110
+ - gte: 1000
111
+ lte: 1300
112
+ ```
113
+
114
+ The `yaml-language-server` comment at the top is optional but recommended; it enables autocompletion and validation of the test definitions in editors that support the YAML language server.
115
+
116
+ For the full set of scenarios, datasets, and predicates, refer to [Testing and Comparing Your Model](https://github.com/climateinteractive/SDEverywhere/wiki/Testing-and-Comparing-Your-Model) in the [SDEverywhere wiki](https://github.com/climateinteractive/SDEverywhere/wiki).
117
+
118
+ If you prefer to define tests programmatically instead of in YAML, use the `testConfigPath` option to point at a JS file that exports a `getConfigOptions` function; when that option is set, the YAML files are not scanned.
119
+
120
+ ### Working locally with `sde dev`
121
+
122
+ When you run `sde dev`, the plugin starts a local dev server (at `http://localhost:8081` by default) that serves the model-check report.
123
+ As you edit your model or your test YAML files, the tests are re-run in the browser and the report refreshes automatically.
124
+
125
+ Each time the model is rebuilt in development mode, the previous bundle is copied to `bundles/previous.js` in your project directory, so you can immediately compare your latest changes against the version you had a moment ago.
126
+ Any other bundle you place in the `bundles` directory is also available for selection in the report, which makes it easy to compare against a known-good version of your model.
127
+
128
+ Use the `serverPort` option to use a custom port:
129
+
130
+ ```js
131
+ checkPlugin({
132
+ serverPort: 8082
133
+ })
134
+ ```
135
+
136
+ ### Comparing against a baseline in production builds
137
+
138
+ For production builds, use the `baseline` and `current` options to tell the plugin which two bundles to compare.
139
+ The `baseline` bundle is usually the bundle that was produced by the last build of your main branch, and can be loaded from a local path or from a URL:
140
+
141
+ ```js
142
+ const deployBaseUrl = 'https://your-username.github.io/your-repo'
143
+
144
+ let baselineBundle
145
+ let currentBundle
146
+ if (process.env.NODE_ENV !== 'development') {
147
+ baselineBundle = {
148
+ name: 'main',
149
+ url: `${deployBaseUrl}/branch/main/extras/check-bundle.js`
150
+ }
151
+ currentBundle = {
152
+ name: process.env.GITHUB_REF_NAME || 'current'
153
+ }
154
+ }
155
+
156
+ // ...
157
+
158
+ checkPlugin({
159
+ // The "left" bundle in comparisons; if undefined, comparison tests are skipped
160
+ baseline: baselineBundle,
161
+
162
+ // The "right" bundle in comparisons; if undefined, the bundle generated by this
163
+ // build is used
164
+ current: currentBundle,
165
+
166
+ // The list of bundles that can be selected in the report when running locally
167
+ remoteBundlesUrl: `${deployBaseUrl}/metadata/bundles.json`
168
+ })
169
+ ```
170
+
171
+ Notes on this configuration:
172
+
173
+ - If the `baseline` bundle cannot be loaded (for example, the first time you build, before any bundle has been published), the build logs a warning, runs the check tests, and skips the comparisons.
174
+ - If the `baseline` bundle was produced by a different version of SDEverywhere than the `current` bundle, comparisons are skipped as well.
175
+ - Use `fetchRemoteBundle` if loading the baseline bundle requires custom logic, such as an authorization header.
176
+
177
+ The [`@sdeverywhere/plugin-deploy`](https://github.com/climateinteractive/SDEverywhere/tree/main/packages/plugin-deploy) package publishes both `check-bundle.js` and `metadata/bundles.json` in the layout assumed above, so the two plugins are designed to be used together.
178
+
179
+ ### Build products
180
+
181
+ In a production build (`sde bundle`), the plugin writes the following to the `sde-prep` directory:
182
+
183
+ ```
184
+ sde-prep/
185
+ ├── check-bundle.js # The bundle for the current version of the model
186
+ └── check-report/ # The generated model-check report (a static web app)
187
+ ```
188
+
189
+ Use the `reportPath` option to write the report somewhere else.
190
+ If any check test fails, `sde bundle` exits with a non-zero status, which causes the build to fail in CI.
26
191
 
27
192
  ## Documentation
28
193
 
package/package.json CHANGED
@@ -1,8 +1,7 @@
1
1
  {
2
2
  "name": "@sdeverywhere/plugin-check",
3
- "version": "0.3.35",
3
+ "version": "0.3.36",
4
4
  "files": [
5
- "bin/**",
6
5
  "dist/**",
7
6
  "template-bundle/**",
8
7
  "template-report/**",
@@ -19,16 +18,13 @@
19
18
  "require": "./dist/index.cjs"
20
19
  }
21
20
  },
22
- "bin": {
23
- "sde-check": "bin/sde-check.js"
24
- },
25
21
  "dependencies": {
26
22
  "@rollup/plugin-node-resolve": "^16.0.3",
27
23
  "@rollup/plugin-replace": "^6.0.3",
28
24
  "@sdeverywhere/check-core": "^0.1.13",
29
25
  "@sdeverywhere/check-ui-shell": "^0.2.25",
30
- "@sdeverywhere/runtime": "^0.2.9",
31
- "@sdeverywhere/runtime-async": "^0.2.9",
26
+ "@sdeverywhere/runtime": "^0.2.10",
27
+ "@sdeverywhere/runtime-async": "^0.2.10",
32
28
  "assert-never": "^1.2.1",
33
29
  "chokidar": "^5.0.0",
34
30
  "picocolors": "^1.0.0",
package/bin/sde-check.js DELETED
@@ -1,54 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /*
4
- * This is the entrypoint for the `sde-check` CLI, which provides commands
5
- * that work in conjunction with `@sdeverywhere/plugin-check`.
6
- */
7
-
8
- import { copyFileSync, existsSync, mkdirSync } from 'fs'
9
- import { join as joinPath } from 'path'
10
-
11
- function printUsage() {
12
- console.log()
13
- console.log('Usage:')
14
- console.log(' sde-check save-current-bundle\t\tcopy the latest bundle to the `bundles` directory')
15
- console.log()
16
- }
17
-
18
- function timestamp() {
19
- const padL = (nr, len = 2, chr = `0`) => `${nr}`.padStart(len, chr)
20
-
21
- const d = new Date()
22
- const year = d.getFullYear()
23
- const month = padL(d.getMonth() + 1)
24
- const day = padL(d.getDate())
25
- const hours = padL(d.getHours())
26
- const minutes = padL(d.getMinutes())
27
- const seconds = padL(d.getSeconds())
28
-
29
- return `${year}-${month}-${day}--${hours}-${minutes}-${seconds}`
30
- }
31
-
32
- const args = process.argv.slice(2)
33
- if (args.length !== 2 || args[0] !== 'save-current-bundle') {
34
- printUsage()
35
- process.exit(1)
36
- }
37
-
38
- // TODO: For now we make a number of assumptions (e.g., that the bundle will be copied to
39
- // the `bundles` directory under the current working directory, that the bundle filename
40
- // will contain the current timestamp); we should make these configurable
41
- const prepDir = joinPath(process.cwd(), 'sde-prep')
42
- const srcBundleFile = joinPath(prepDir, 'check-bundle.js')
43
- if (!existsSync(srcBundleFile)) {
44
- console.error(`ERROR: No 'check-bundle.js' file found in 'sde-prep' directory`)
45
- process.exit(1)
46
- }
47
-
48
- const bundlesDir = joinPath(process.cwd(), 'bundles')
49
- if (!existsSync(bundlesDir)) {
50
- mkdirSync(bundlesDir, { recursive: true })
51
- }
52
-
53
- const dstBundleFile = joinPath(bundlesDir, `${timestamp()}.js`)
54
- copyFileSync(srcBundleFile, dstBundleFile)