@pikku/core 0.12.121 → 0.12.122
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/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## 0.12.122
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 3511717: A virtual user run refused for an unknown or acted-upon persona now answers 400, and one refused for a probing disposition in production answers 403, carrying the reason instead of a bare 500 errorId.
|
|
6
|
+
- 3511717: Enabling a virtual user schedule whose disposition production refuses is now refused when written (403), rather than saved and then failing on every tick with nobody watching. Disabling one, or editing one that is off, is always allowed.
|
|
7
|
+
|
|
1
8
|
## 0.12.121
|
|
2
9
|
|
|
3
10
|
### Patch Changes
|
|
@@ -210,15 +210,26 @@ export interface WriteVirtualUserScheduleParams {
|
|
|
210
210
|
minIntervalMs?: number;
|
|
211
211
|
maxIntervalMs?: number;
|
|
212
212
|
nextRunAt?: string;
|
|
213
|
+
/** The same three signals `startVirtualUserRun` reads to decide production. */
|
|
214
|
+
config?: {
|
|
215
|
+
nodeEnv?: string;
|
|
216
|
+
};
|
|
217
|
+
environments?: Readonly<Record<string, PersonaEnvironment>>;
|
|
218
|
+
environment?: string;
|
|
213
219
|
}
|
|
214
220
|
/**
|
|
215
221
|
* Writes a persona's cadence.
|
|
216
222
|
*
|
|
217
|
-
* Applies the same
|
|
223
|
+
* Applies the same rules `startVirtualUserRun` enforces, at the point the row is
|
|
218
224
|
* written rather than every hour afterwards: an acted-upon persona has no
|
|
219
|
-
* session,
|
|
225
|
+
* session, and an enabled cadence whose disposition production refuses, so
|
|
226
|
+
* either is a tick that can only ever fail to start, with nobody watching.
|
|
227
|
+
*
|
|
228
|
+
* The disposition checked is the one the row will hold, since a field left out
|
|
229
|
+
* keeps what it had: enabling a row saved earlier as adversarial is refused
|
|
230
|
+
* just as saving one would be.
|
|
220
231
|
*/
|
|
221
|
-
export declare const writeVirtualUserSchedule: ({ store, personas, persona, enabled, disposition, goals, budget, minIntervalMs, maxIntervalMs, nextRunAt, }: WriteVirtualUserScheduleParams) => Promise<VirtualUserScheduleRecord>;
|
|
232
|
+
export declare const writeVirtualUserSchedule: ({ store, personas, persona, enabled, disposition, goals, budget, minIntervalMs, maxIntervalMs, nextRunAt, config, environments, environment, }: WriteVirtualUserScheduleParams) => Promise<VirtualUserScheduleRecord>;
|
|
222
233
|
/** What a due schedule asks `runVirtualUser` for. */
|
|
223
234
|
export declare const virtualUserScheduleRunInput: (schedule: VirtualUserScheduleRecord) => {
|
|
224
235
|
persona: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BadRequestError, ForbiddenError } from '../../errors/errors.js';
|
|
1
2
|
import { prepareVirtualUserRun } from './prepare-virtual-user-run.js';
|
|
2
3
|
import { runVirtualUser as runVirtualUserEngine } from './run-virtual-user.js';
|
|
3
4
|
import { personaVirtualUserTarget } from './virtual-user-target.js';
|
|
@@ -59,10 +60,10 @@ export const signInPathFor = (configured, plugin) => {
|
|
|
59
60
|
export const runnablePersona = (personas, personaId) => {
|
|
60
61
|
const persona = personas[personaId];
|
|
61
62
|
if (!persona) {
|
|
62
|
-
throw new
|
|
63
|
+
throw new BadRequestError(`Unknown persona "${personaId}" — declare it with definePersonas()`);
|
|
63
64
|
}
|
|
64
65
|
if (!persona.runnable) {
|
|
65
|
-
throw new
|
|
66
|
+
throw new BadRequestError(`Persona "${personaId}" is declared as acted upon, never run`);
|
|
66
67
|
}
|
|
67
68
|
return persona;
|
|
68
69
|
};
|
|
@@ -106,6 +107,14 @@ const isProductionRun = (config, environments, environment) => {
|
|
|
106
107
|
}
|
|
107
108
|
return environment ? Boolean(environments[environment]?.production) : true;
|
|
108
109
|
};
|
|
110
|
+
// Every disposition other than this one exists to find out what the product
|
|
111
|
+
// does wrong, which is not a thing to do to real customers' data.
|
|
112
|
+
const refuseInProduction = (personaId, disposition, config, environments, environment) => {
|
|
113
|
+
if (disposition !== PRODUCTION_DISPOSITION &&
|
|
114
|
+
isProductionRun(config, environments, environment)) {
|
|
115
|
+
throw new ForbiddenError(`Only the '${PRODUCTION_DISPOSITION}' disposition may run against production; "${personaId}" is ${disposition}`);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
109
118
|
/**
|
|
110
119
|
* Resolves a request against the declaration and records the run.
|
|
111
120
|
*
|
|
@@ -119,13 +128,9 @@ export const startVirtualUserRun = async ({ store, personas, config, environment
|
|
|
119
128
|
const disposition = (requested ??
|
|
120
129
|
persona.disposition ??
|
|
121
130
|
'realistic');
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
if (disposition !== PRODUCTION_DISPOSITION &&
|
|
126
|
-
isProductionRun(config, environments, environment)) {
|
|
127
|
-
throw new Error(`Only the '${PRODUCTION_DISPOSITION}' disposition may run against production; "${personaId}" is ${disposition}`);
|
|
128
|
-
}
|
|
131
|
+
// Checked against the effective disposition, so an override cannot smuggle
|
|
132
|
+
// one in.
|
|
133
|
+
refuseInProduction(personaId, disposition, config, environments, environment);
|
|
129
134
|
// Seeded here rather than inside the engine so the record carries the seed
|
|
130
135
|
// even if the run dies before returning — an unreproducible crash costs the
|
|
131
136
|
// most.
|
|
@@ -237,17 +242,29 @@ export const serializeVirtualUserSchedule = (schedule, personas) => {
|
|
|
237
242
|
/**
|
|
238
243
|
* Writes a persona's cadence.
|
|
239
244
|
*
|
|
240
|
-
* Applies the same
|
|
245
|
+
* Applies the same rules `startVirtualUserRun` enforces, at the point the row is
|
|
241
246
|
* written rather than every hour afterwards: an acted-upon persona has no
|
|
242
|
-
* session,
|
|
247
|
+
* session, and an enabled cadence whose disposition production refuses, so
|
|
248
|
+
* either is a tick that can only ever fail to start, with nobody watching.
|
|
249
|
+
*
|
|
250
|
+
* The disposition checked is the one the row will hold, since a field left out
|
|
251
|
+
* keeps what it had: enabling a row saved earlier as adversarial is refused
|
|
252
|
+
* just as saving one would be.
|
|
243
253
|
*/
|
|
244
|
-
export const writeVirtualUserSchedule = async ({ store, personas, persona, enabled, disposition, goals, budget, minIntervalMs, maxIntervalMs, nextRunAt, }) => {
|
|
254
|
+
export const writeVirtualUserSchedule = async ({ store, personas, persona, enabled, disposition, goals, budget, minIntervalMs, maxIntervalMs, nextRunAt, config, environments, environment = process.env.PIKKU_ENV, }) => {
|
|
245
255
|
const scheduleStore = requireVirtualUserScheduleStore(store);
|
|
246
|
-
runnablePersona(personas, persona);
|
|
256
|
+
const declared = runnablePersona(personas, persona);
|
|
257
|
+
const current = enabled === undefined || disposition === undefined
|
|
258
|
+
? await scheduleStore.get(persona)
|
|
259
|
+
: null;
|
|
260
|
+
const created = disposition === undefined && !current ? declared.disposition : undefined;
|
|
261
|
+
if (enabled ?? current?.enabled) {
|
|
262
|
+
refuseInProduction(persona, disposition ?? current?.disposition ?? created ?? 'realistic', config, environments, environment);
|
|
263
|
+
}
|
|
247
264
|
return scheduleStore.set({
|
|
248
265
|
persona,
|
|
249
266
|
enabled,
|
|
250
|
-
disposition: disposition,
|
|
267
|
+
disposition: (disposition ?? created),
|
|
251
268
|
goals,
|
|
252
269
|
budget: budget === undefined
|
|
253
270
|
? undefined
|