@dotsetlabs/bellwether 2.1.2 → 2.1.3

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +2 -2
  3. package/dist/baseline/golden-output.d.ts +0 -4
  4. package/dist/baseline/golden-output.js +2 -47
  5. package/dist/cli/commands/baseline-accept.js +14 -45
  6. package/dist/cli/commands/baseline.js +23 -78
  7. package/dist/cli/commands/check-formatters.d.ts +10 -0
  8. package/dist/cli/commands/check-formatters.js +160 -0
  9. package/dist/cli/commands/check.js +33 -241
  10. package/dist/cli/commands/contract.js +1 -13
  11. package/dist/cli/commands/explore.js +19 -66
  12. package/dist/cli/commands/watch.js +2 -3
  13. package/dist/cli/output.d.ts +0 -42
  14. package/dist/cli/output.js +73 -110
  15. package/dist/cli/utils/config-loader.d.ts +6 -0
  16. package/dist/cli/utils/config-loader.js +19 -0
  17. package/dist/cli/utils/error-hints.d.ts +9 -0
  18. package/dist/cli/utils/error-hints.js +128 -0
  19. package/dist/cli/utils/headers.js +2 -25
  20. package/dist/cli/utils/path-resolution.d.ts +10 -0
  21. package/dist/cli/utils/path-resolution.js +27 -0
  22. package/dist/cli/utils/report-loader.d.ts +9 -0
  23. package/dist/cli/utils/report-loader.js +31 -0
  24. package/dist/cli/utils/server-runtime.d.ts +16 -0
  25. package/dist/cli/utils/server-runtime.js +31 -0
  26. package/dist/config/defaults.d.ts +0 -1
  27. package/dist/config/defaults.js +0 -1
  28. package/dist/constants/core.d.ts +0 -42
  29. package/dist/constants/core.js +0 -50
  30. package/dist/contract/validator.js +2 -47
  31. package/dist/interview/question-category.d.ts +5 -0
  32. package/dist/interview/question-category.js +2 -0
  33. package/dist/interview/question-types.d.ts +80 -0
  34. package/dist/interview/question-types.js +2 -0
  35. package/dist/interview/schema-test-generator.d.ts +3 -29
  36. package/dist/interview/schema-test-generator.js +11 -286
  37. package/dist/interview/test-fixtures.d.ts +19 -0
  38. package/dist/interview/test-fixtures.js +2 -0
  39. package/dist/interview/types.d.ts +5 -80
  40. package/dist/persona/types.d.ts +3 -5
  41. package/dist/scenarios/types.d.ts +1 -1
  42. package/dist/transport/auth-errors.d.ts +15 -0
  43. package/dist/transport/auth-errors.js +22 -0
  44. package/dist/transport/http-transport.js +7 -9
  45. package/dist/transport/mcp-client.d.ts +0 -4
  46. package/dist/transport/mcp-client.js +13 -37
  47. package/dist/transport/sse-transport.d.ts +0 -1
  48. package/dist/transport/sse-transport.js +13 -28
  49. package/dist/utils/content-type.d.ts +14 -0
  50. package/dist/utils/content-type.js +37 -0
  51. package/dist/utils/http-headers.d.ts +9 -0
  52. package/dist/utils/http-headers.js +34 -0
  53. package/dist/utils/smart-truncate.js +2 -23
  54. package/package.json +2 -2
@@ -63,8 +63,6 @@ export declare const TRANSPORT_ERRORS: {
63
63
  readonly MAX_ERRORS_TO_COLLECT: 50;
64
64
  /** Error messages that indicate a likely server bug (vs environment issue) */
65
65
  readonly SERVER_BUG_INDICATORS: readonly RegExp[];
66
- /** Error messages that indicate environment/config issues */
67
- readonly ENVIRONMENT_ISSUE_INDICATORS: readonly RegExp[];
68
66
  };
69
67
  /**
70
68
  * Interview process configuration defaults.
@@ -508,46 +506,6 @@ export declare const CLI_SECURITY: {
508
506
  /**
509
507
  * External service URLs.
510
508
  */
511
- export declare const TOKEN_ESTIMATION: {
512
- /** Average input tokens per question */
513
- readonly AVG_INPUT_PER_QUESTION: 500;
514
- /** Average output tokens per question */
515
- readonly AVG_OUTPUT_PER_QUESTION: 300;
516
- /** Schema overhead tokens per tool */
517
- readonly SCHEMA_OVERHEAD_PER_TOOL: 200;
518
- /** Character to token ratio (approximate) */
519
- readonly CHARS_PER_TOKEN: 4;
520
- /** Word adjustment factor */
521
- readonly WORD_ADJUSTMENT: 0.3;
522
- /** Role/message overhead tokens */
523
- readonly MESSAGE_OVERHEAD_TOKENS: 4;
524
- /** Default context window when model unknown */
525
- readonly DEFAULT_CONTEXT_WINDOW: 16000;
526
- };
527
- /**
528
- * Default token budget limits.
529
- */
530
- export declare const TOKEN_BUDGET: {
531
- /** Maximum total tokens for an interview */
532
- readonly MAX_TOTAL_TOKENS: 1000000;
533
- /** Maximum input tokens per request */
534
- readonly MAX_INPUT_PER_REQUEST: 100000;
535
- /** Maximum output tokens per request */
536
- readonly MAX_OUTPUT_PER_REQUEST: 8000;
537
- /** Reserved tokens for output in context */
538
- readonly OUTPUT_RESERVE: 4000;
539
- };
540
- /**
541
- * Metrics collection configuration.
542
- */
543
- export declare const METRICS_CONFIG: {
544
- /** Maximum entries in metrics store */
545
- readonly MAX_ENTRIES: 10000;
546
- /** Latency histogram buckets (ms) */
547
- readonly LATENCY_BUCKETS: readonly [10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000];
548
- /** Metric name prefix */
549
- readonly PREFIX: "bellwether_";
550
- };
551
509
  /**
552
510
  * Default model configurations per provider.
553
511
  * Uses budget-friendly, non-reasoning models by default for cost efficiency.
@@ -112,14 +112,6 @@ export const TRANSPORT_ERRORS = {
112
112
  /protocol.*error/i,
113
113
  /invalid.*response/i,
114
114
  ],
115
- /** Error messages that indicate environment/config issues */
116
- ENVIRONMENT_ISSUE_INDICATORS: [
117
- /ENOENT/i,
118
- /not found/i,
119
- /permission denied/i,
120
- /EACCES/i,
121
- /cannot find module/i,
122
- ],
123
115
  };
124
116
  // ==================== Interview Configuration ====================
125
117
  /**
@@ -589,48 +581,6 @@ export const CLI_SECURITY = {
589
581
  /**
590
582
  * External service URLs.
591
583
  */
592
- export const TOKEN_ESTIMATION = {
593
- /** Average input tokens per question */
594
- AVG_INPUT_PER_QUESTION: 500,
595
- /** Average output tokens per question */
596
- AVG_OUTPUT_PER_QUESTION: 300,
597
- /** Schema overhead tokens per tool */
598
- SCHEMA_OVERHEAD_PER_TOOL: 200,
599
- /** Character to token ratio (approximate) */
600
- CHARS_PER_TOKEN: 4,
601
- /** Word adjustment factor */
602
- WORD_ADJUSTMENT: 0.3,
603
- /** Role/message overhead tokens */
604
- MESSAGE_OVERHEAD_TOKENS: 4,
605
- /** Default context window when model unknown */
606
- DEFAULT_CONTEXT_WINDOW: 16000,
607
- };
608
- // ==================== Token Budget ====================
609
- /**
610
- * Default token budget limits.
611
- */
612
- export const TOKEN_BUDGET = {
613
- /** Maximum total tokens for an interview */
614
- MAX_TOTAL_TOKENS: 1000000,
615
- /** Maximum input tokens per request */
616
- MAX_INPUT_PER_REQUEST: 100000,
617
- /** Maximum output tokens per request */
618
- MAX_OUTPUT_PER_REQUEST: 8000,
619
- /** Reserved tokens for output in context */
620
- OUTPUT_RESERVE: 4000,
621
- };
622
- // ==================== Metrics ====================
623
- /**
624
- * Metrics collection configuration.
625
- */
626
- export const METRICS_CONFIG = {
627
- /** Maximum entries in metrics store */
628
- MAX_ENTRIES: 10000,
629
- /** Latency histogram buckets (ms) */
630
- LATENCY_BUCKETS: [10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000],
631
- /** Metric name prefix */
632
- PREFIX: 'bellwether_',
633
- };
634
584
  // ==================== LLM Models ====================
635
585
  /**
636
586
  * Default model configurations per provider.
@@ -8,6 +8,8 @@
8
8
  import { readFileSync, existsSync } from 'fs';
9
9
  import * as yaml from 'yaml';
10
10
  import { CONTRACT_TESTING } from '../constants.js';
11
+ import { getValueAtPath } from '../utils/jsonpath.js';
12
+ import { detectContentType } from '../utils/content-type.js';
11
13
  /**
12
14
  * Load a contract from a file.
13
15
  */
@@ -398,33 +400,6 @@ function validateOutput(toolName, result, outputContract) {
398
400
  }
399
401
  return violations;
400
402
  }
401
- /**
402
- * Get value at a JSONPath-like path.
403
- * Supports simple paths like $.field.nested[0].value
404
- */
405
- function getValueAtPath(obj, path) {
406
- // Remove leading $. if present
407
- const cleanPath = path.replace(/^\$\.?/, '');
408
- if (!cleanPath)
409
- return obj;
410
- const segments = cleanPath.split(/\.|\[|\]/).filter(Boolean);
411
- let current = obj;
412
- for (const segment of segments) {
413
- if (current === null || current === undefined)
414
- return undefined;
415
- if (typeof current !== 'object')
416
- return undefined;
417
- // Handle array index
418
- const index = parseInt(segment, 10);
419
- if (!isNaN(index) && Array.isArray(current)) {
420
- current = current[index];
421
- }
422
- else {
423
- current = current[segment];
424
- }
425
- }
426
- return current;
427
- }
428
403
  /**
429
404
  * Get the type name of a value.
430
405
  */
@@ -435,26 +410,6 @@ function getValueType(value) {
435
410
  return 'array';
436
411
  return typeof value;
437
412
  }
438
- /**
439
- * Detect content type from raw output.
440
- */
441
- function detectContentType(raw) {
442
- const trimmed = raw.trim();
443
- if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
444
- (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
445
- try {
446
- JSON.parse(trimmed);
447
- return 'json';
448
- }
449
- catch {
450
- // Not valid JSON
451
- }
452
- }
453
- if (/^#|^\*{1,3}[^*]|\[.*\]\(.*\)|^```/.test(trimmed)) {
454
- return 'markdown';
455
- }
456
- return 'text';
457
- }
458
413
  /**
459
414
  * Truncate a string for display.
460
415
  */
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Canonical question categories used across personas, interview generation, and scenarios.
3
+ */
4
+ export type QuestionCategory = 'happy_path' | 'edge_case' | 'error_handling' | 'boundary' | 'security';
5
+ //# sourceMappingURL=question-category.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=question-category.js.map
@@ -0,0 +1,80 @@
1
+ import type { QuestionCategory } from './question-category.js';
2
+ /**
3
+ * Expected outcome for a test question.
4
+ * - 'success': Test expects the tool to execute successfully
5
+ * - 'error': Test expects the tool to reject/fail (validation test)
6
+ * - 'either': Test outcome is acceptable either way
7
+ */
8
+ export type ExpectedOutcome = 'success' | 'error' | 'either';
9
+ /**
10
+ * A question to ask about a tool's behavior.
11
+ */
12
+ export interface InterviewQuestion {
13
+ /** Description of what this question tests */
14
+ description: string;
15
+ /** Category of question */
16
+ category: QuestionCategory;
17
+ /** Arguments to pass to the tool */
18
+ args: Record<string, unknown>;
19
+ /**
20
+ * Expected outcome of this test.
21
+ * Used to determine if the tool behaved correctly.
22
+ * - 'success': Expects successful execution (happy path)
23
+ * - 'error': Expects rejection/error (validation test)
24
+ * - 'either': Either outcome is acceptable
25
+ */
26
+ expectedOutcome?: ExpectedOutcome;
27
+ /** Semantic validation metadata (for tests generated from semantic type inference) */
28
+ metadata?: {
29
+ /** The inferred semantic type being tested */
30
+ semanticType?: string;
31
+ /** Expected behavior: 'reject' for invalid values, 'accept' for valid */
32
+ expectedBehavior?: 'reject' | 'accept';
33
+ /** Confidence level of the semantic type inference (0-1) */
34
+ confidence?: number;
35
+ /** Stateful testing metadata */
36
+ stateful?: {
37
+ /** Keys injected from prior tool outputs */
38
+ usedKeys?: string[];
39
+ /** Keys captured from this response */
40
+ providedKeys?: string[];
41
+ };
42
+ /**
43
+ * Whether this tool uses operation-based dispatch pattern.
44
+ * Tools with this pattern have different required args per operation.
45
+ */
46
+ operationBased?: boolean;
47
+ /** The parameter name that selects the operation (e.g., "operation", "action") */
48
+ operationParam?: string;
49
+ /** The parameter name that holds operation-specific args (e.g., "args", "params") */
50
+ argsParam?: string;
51
+ /**
52
+ * Whether this tool requires prior state (session, chain, etc.).
53
+ * These tools need an active session before they can work.
54
+ */
55
+ selfStateful?: boolean;
56
+ /** Reason for self-stateful detection */
57
+ selfStatefulReason?: string;
58
+ /**
59
+ * Whether this tool has complex array schemas requiring structured data.
60
+ * Simple test data generation often fails for these tools.
61
+ */
62
+ hasComplexArrays?: boolean;
63
+ /** Array parameters with complex item schemas */
64
+ complexArrayParams?: string[];
65
+ };
66
+ }
67
+ /**
68
+ * Assessment of whether a tool interaction outcome matched expectations.
69
+ */
70
+ export interface OutcomeAssessment {
71
+ /** What outcome was expected */
72
+ expected: ExpectedOutcome;
73
+ /** What actually happened */
74
+ actual: 'success' | 'error';
75
+ /** Whether the tool behaved correctly (matches expectation) */
76
+ correct: boolean;
77
+ /** True if this was a validation test that correctly rejected invalid input */
78
+ isValidationSuccess?: boolean;
79
+ }
80
+ //# sourceMappingURL=question-types.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=question-types.js.map
@@ -6,27 +6,10 @@
6
6
  * 8-12 tests per tool covering boundaries, types, enums, and error handling.
7
7
  */
8
8
  import type { MCPTool } from '../transport/types.js';
9
- import type { InterviewQuestion, ExpectedOutcome } from './types.js';
10
- import type { QuestionCategory } from '../persona/types.js';
9
+ import type { InterviewQuestion } from './types.js';
11
10
  import type { SemanticInference } from '../validation/semantic-types.js';
12
- /**
13
- * Test fixture pattern for matching parameter names.
14
- */
15
- export interface TestFixturePattern {
16
- /** Regex pattern to match parameter names */
17
- match: string;
18
- /** Value to use for matching parameters */
19
- value: unknown;
20
- }
21
- /**
22
- * Test fixtures configuration for customizing test values.
23
- */
24
- export interface TestFixturesConfig {
25
- /** Custom values for specific parameter names (exact match) */
26
- parameterValues?: Record<string, unknown>;
27
- /** Custom values for parameters matching regex patterns */
28
- patterns?: TestFixturePattern[];
29
- }
11
+ import type { TestFixturesConfig } from './test-fixtures.js';
12
+ export type { TestFixturePattern, TestFixturesConfig } from './test-fixtures.js';
30
13
  /**
31
14
  * Options for test generation.
32
15
  */
@@ -49,15 +32,6 @@ export interface SchemaTestGeneratorResult {
49
32
  /** Semantic type inferences for parameters */
50
33
  semanticInferences: SemanticInference[];
51
34
  }
52
- /**
53
- * Determine expected outcome for a test based on its category and description.
54
- * Uses OUTCOME_ASSESSMENT constants to classify tests.
55
- *
56
- * @param category - Test category
57
- * @param description - Test description
58
- * @returns Expected outcome: 'success', 'error', or 'either'
59
- */
60
- export declare function determineExpectedOutcome(category: QuestionCategory, description: string): ExpectedOutcome;
61
35
  /**
62
36
  * Generate comprehensive schema-based test cases for a tool.
63
37
  *
@@ -5,9 +5,10 @@
5
5
  * This module is the core of the enhanced testing capability, producing
6
6
  * 8-12 tests per tool covering boundaries, types, enums, and error handling.
7
7
  */
8
- import { SCHEMA_TESTING, SEMANTIC_VALIDATION, OUTCOME_ASSESSMENT } from '../constants.js';
9
- import { SMART_VALUE_GENERATION, OPERATION_BASED_DETECTION, SELF_STATEFUL_DETECTION, COMPLEX_SCHEMA_DETECTION, } from '../constants/testing.js';
8
+ import { SCHEMA_TESTING, SEMANTIC_VALIDATION } from '../constants.js';
9
+ import { OPERATION_BASED_DETECTION, SELF_STATEFUL_DETECTION, COMPLEX_SCHEMA_DETECTION, } from '../constants/testing.js';
10
10
  import { generateSemanticTests } from '../validation/semantic-test-generator.js';
11
+ import { generateSmartStringValue as generateSmartStringValueResult, generateSmartNumberValue as generateSmartNumberValueResult, } from './smart-value-generator.js';
11
12
  // ==================== Helper Functions ====================
12
13
  /**
13
14
  * Get the primary type from a schema property.
@@ -107,39 +108,12 @@ function mergeAllOfSchemas(schemas) {
107
108
  * Used for nested array items where we don't have a property name.
108
109
  */
109
110
  function generateSmartStringValueForSchema(schema) {
110
- const description = (schema.description ?? '').toLowerCase();
111
- // Check description for explicit format hints
112
- for (const { pattern, value } of DATE_FORMAT_PATTERNS) {
113
- if (pattern.test(schema.description ?? '') || pattern.test(description)) {
114
- return value;
115
- }
116
- }
117
- for (const { pattern, value } of SEMANTIC_FORMAT_PATTERNS) {
118
- if (pattern.test(schema.description ?? '') || pattern.test(description)) {
119
- return value;
120
- }
111
+ const generated = generateSmartStringValueResult('', schema).value;
112
+ if (typeof generated === 'string' && generated.length > 0) {
113
+ return generated;
121
114
  }
122
- // Check schema format field
123
- if (schema.format === 'date')
124
- return '2024-01-15';
125
- if (schema.format === 'date-time')
126
- return '2024-01-15T14:30:00Z';
127
- if (schema.format === 'email')
128
- return 'test@example.com';
129
- if (schema.format === 'uri' || schema.format === 'url')
130
- return 'https://example.com';
131
- if (schema.format === 'uuid')
132
- return '550e8400-e29b-41d4-a716-446655440000';
133
- if (schema.format === 'ipv4')
134
- return '192.168.1.100';
135
- if (schema.format === 'time')
136
- return '14:30:00';
137
- // Respect minLength constraint
138
115
  const minLength = schema.minLength ?? 0;
139
- if (minLength > 4) {
140
- return 'test'.padEnd(minLength, 'x');
141
- }
142
- return 'test';
116
+ return minLength > 4 ? 'test'.padEnd(minLength, 'x') : 'test';
143
117
  }
144
118
  /**
145
119
  * Generate a minimal array that satisfies minItems constraint.
@@ -373,266 +347,17 @@ function generateDefaultValue(propName, prop, fixtures) {
373
347
  return 'test';
374
348
  }
375
349
  }
376
- /**
377
- * Pattern matchers for detecting date/time formats in descriptions.
378
- * Each pattern maps to a format string and example value.
379
- */
380
- const DATE_FORMAT_PATTERNS = [
381
- // ISO 8601 date patterns
382
- {
383
- pattern: /YYYY-MM-DD|ISO\s*8601\s*date|date.*format.*YYYY/i,
384
- value: '2024-01-15',
385
- formatName: 'ISO 8601 date',
386
- },
387
- { pattern: /YYYY-MM|year-month|month.*format/i, value: '2024-01', formatName: 'year-month' },
388
- {
389
- pattern: /ISO\s*8601\s*(datetime|timestamp)|datetime.*format|timestamp.*ISO/i,
390
- value: '2024-01-15T14:30:00Z',
391
- formatName: 'ISO 8601 datetime',
392
- },
393
- // Unix timestamp patterns
394
- {
395
- pattern: /unix\s*timestamp|epoch\s*time|seconds\s*since/i,
396
- value: '1705330200',
397
- formatName: 'Unix timestamp',
398
- },
399
- {
400
- pattern: /milliseconds?\s*(since|timestamp)|ms\s*timestamp/i,
401
- value: '1705330200000',
402
- formatName: 'Unix timestamp (ms)',
403
- },
404
- // Time patterns
405
- {
406
- pattern: /HH:MM:SS|time.*format.*HH|24.hour.*time/i,
407
- value: '14:30:00',
408
- formatName: '24-hour time',
409
- },
410
- { pattern: /HH:MM|hour.*minute/i, value: '14:30', formatName: 'hour:minute' },
411
- // Other date formats
412
- { pattern: /MM\/DD\/YYYY|US\s*date/i, value: '01/15/2024', formatName: 'US date' },
413
- { pattern: /DD\/MM\/YYYY|European\s*date/i, value: '15/01/2024', formatName: 'European date' },
414
- ];
415
- /**
416
- * Pattern matchers for detecting other semantic types in descriptions.
417
- */
418
- const SEMANTIC_FORMAT_PATTERNS = [
419
- // Currency/money patterns
420
- { pattern: /currency.*amount|dollar.*amount|price/i, value: '99.99', formatName: 'currency' },
421
- { pattern: /percentage|percent/i, value: '50', formatName: 'percentage' },
422
- // Phone patterns
423
- { pattern: /phone.*number|telephone/i, value: '+1-555-123-4567', formatName: 'phone' },
424
- // UUID patterns
425
- {
426
- pattern: /UUID|unique.*identifier/i,
427
- value: '550e8400-e29b-41d4-a716-446655440000',
428
- formatName: 'UUID',
429
- },
430
- // IP address patterns
431
- { pattern: /IP.*address|IPv4/i, value: '192.168.1.100', formatName: 'IP address' },
432
- // JSON patterns
433
- { pattern: /JSON\s*string|stringify|serialized/i, value: '{"key": "value"}', formatName: 'JSON' },
434
- // Base64 patterns
435
- { pattern: /base64|encoded/i, value: 'dGVzdA==', formatName: 'base64' },
436
- ];
437
- /**
438
- * Generate a contextually appropriate string value based on property name,
439
- * constraints, and description.
440
- *
441
- * This function implements smart test value generation by:
442
- * 1. Parsing schema descriptions for format hints (e.g., "YYYY-MM-DD")
443
- * 2. Checking schema format field
444
- * 3. Inferring from property name patterns
445
- */
446
350
  function generateSmartStringValue(propName, prop) {
447
- const lowerName = propName.toLowerCase();
448
- const description = (prop.description ?? '').toLowerCase();
449
- // Priority 1: Check description for explicit date/time format hints
450
- // This is the most reliable indicator since the schema author specified it
451
- for (const { pattern, value } of DATE_FORMAT_PATTERNS) {
452
- if (pattern.test(prop.description ?? '') || pattern.test(description)) {
453
- return value;
454
- }
455
- }
456
- // Priority 2: Check description for other semantic format hints
457
- for (const { pattern, value } of SEMANTIC_FORMAT_PATTERNS) {
458
- if (pattern.test(prop.description ?? '') || pattern.test(description)) {
459
- return value;
460
- }
461
- }
462
- // Priority 3: Check schema format field (JSON Schema standard)
463
- if (prop.format === 'date') {
464
- return '2024-01-15';
465
- }
466
- if (prop.format === 'date-time') {
467
- return '2024-01-15T14:30:00Z';
468
- }
469
- if (prop.format === 'email') {
470
- return 'test@example.com';
471
- }
472
- if (prop.format === 'uri' || prop.format === 'url') {
473
- return 'https://example.com';
474
- }
475
- if (prop.format === 'uuid') {
476
- return '550e8400-e29b-41d4-a716-446655440000';
477
- }
478
- if (prop.format === 'ipv4') {
479
- return '192.168.1.100';
480
- }
481
- if (prop.format === 'time') {
482
- return '14:30:00';
483
- }
484
- // Priority 4: Infer from property name patterns
485
- if (lowerName.includes('date') || description.includes('date')) {
486
- return '2024-01-15';
487
- }
488
- if (lowerName.includes('time') || description.includes('time')) {
489
- return '14:30:00';
490
- }
491
- if (lowerName.includes('email') || description.includes('email')) {
492
- return 'test@example.com';
493
- }
494
- if (lowerName.includes('url') ||
495
- lowerName.includes('uri') ||
496
- description.includes('url') ||
497
- description.includes('uri')) {
498
- return 'https://example.com';
499
- }
500
- if (lowerName.includes('path') ||
501
- lowerName.includes('directory') ||
502
- lowerName.includes('dir') ||
503
- description.includes('path')) {
504
- return '/tmp/test';
505
- }
506
- if (lowerName.includes('id') || description.includes('identifier')) {
507
- return 'test-id-123';
508
- }
509
- if (lowerName.includes('name')) {
510
- return 'test-name';
511
- }
512
- if (lowerName.includes('query') || lowerName.includes('search')) {
513
- return 'test query';
514
- }
515
- if (lowerName.includes('token')) {
516
- return 'test-token-abc123';
517
- }
518
- if (lowerName.includes('account') || description.includes('account')) {
519
- return 'test-account-123';
520
- }
521
- if (lowerName.includes('amount') || description.includes('amount')) {
522
- return '100.00';
523
- }
524
- if (lowerName.includes('category') || description.includes('category')) {
525
- return 'test-category';
526
- }
527
- // Default fallback
528
- return 'test';
351
+ const generated = generateSmartStringValueResult(propName, prop).value;
352
+ return typeof generated === 'string' ? generated : 'test';
529
353
  }
530
354
  /**
531
355
  * Generate a contextually appropriate number value based on constraints and property name.
532
356
  * Detects coordinates (latitude/longitude) and pagination parameters.
533
357
  */
534
358
  function generateSmartNumberValue(prop, propName) {
535
- const { COORDINATES, PAGINATION } = SMART_VALUE_GENERATION;
536
- const lowerName = (propName ?? '').toLowerCase();
537
- const description = (prop.description ?? '').toLowerCase();
538
- // Check for latitude patterns
539
- for (const pattern of COORDINATES.LATITUDE_PATTERNS) {
540
- if (pattern.test(propName ?? '') || pattern.test(description)) {
541
- // Ensure value is within valid latitude range
542
- const value = COORDINATES.DEFAULTS.latitude;
543
- if (prop.minimum !== undefined && value < prop.minimum) {
544
- return prop.minimum;
545
- }
546
- if (prop.maximum !== undefined && value > prop.maximum) {
547
- return prop.maximum;
548
- }
549
- return value;
550
- }
551
- }
552
- // Check for longitude patterns
553
- for (const pattern of COORDINATES.LONGITUDE_PATTERNS) {
554
- if (pattern.test(propName ?? '') || pattern.test(description)) {
555
- // Ensure value is within valid longitude range
556
- const value = COORDINATES.DEFAULTS.longitude;
557
- if (prop.minimum !== undefined && value < prop.minimum) {
558
- return prop.minimum;
559
- }
560
- if (prop.maximum !== undefined && value > prop.maximum) {
561
- return prop.maximum;
562
- }
563
- return value;
564
- }
565
- }
566
- // Check for pagination patterns
567
- for (const pattern of PAGINATION.LIMIT_PATTERNS) {
568
- if (pattern.test(propName ?? '')) {
569
- const value = PAGINATION.DEFAULTS.limit;
570
- if (prop.minimum !== undefined && value < prop.minimum) {
571
- return prop.minimum;
572
- }
573
- if (prop.maximum !== undefined && value > prop.maximum) {
574
- return prop.maximum;
575
- }
576
- return value;
577
- }
578
- }
579
- for (const pattern of PAGINATION.OFFSET_PATTERNS) {
580
- if (pattern.test(propName ?? '')) {
581
- // Distinguish between offset/skip (start at 0) and page (start at 1)
582
- const lowerPropName = (propName ?? '').toLowerCase();
583
- if (lowerPropName === 'page' || lowerPropName.includes('page')) {
584
- return PAGINATION.DEFAULTS.page;
585
- }
586
- return PAGINATION.DEFAULTS.offset;
587
- }
588
- }
589
- // Check for year detection in name/description
590
- if (lowerName.includes('year') || description.includes('year')) {
591
- return 2024;
592
- }
593
- // Check for percentage
594
- if (lowerName.includes('percent') || description.includes('percent')) {
595
- return 50;
596
- }
597
- // Standard value generation based on constraints
598
- const min = prop.minimum ?? 0;
599
- const max = prop.maximum ?? 100;
600
- // Use midpoint between min and max if both are specified
601
- if (prop.minimum !== undefined && prop.maximum !== undefined) {
602
- return Math.floor((min + max) / 2);
603
- }
604
- // Use minimum + 1 if only minimum is specified
605
- if (prop.minimum !== undefined) {
606
- return min + 1;
607
- }
608
- // Use reasonable defaults
609
- return 1;
610
- }
611
- /**
612
- * Determine expected outcome for a test based on its category and description.
613
- * Uses OUTCOME_ASSESSMENT constants to classify tests.
614
- *
615
- * @param category - Test category
616
- * @param description - Test description
617
- * @returns Expected outcome: 'success', 'error', or 'either'
618
- */
619
- export function determineExpectedOutcome(category, description) {
620
- // Check if category expects error
621
- if (OUTCOME_ASSESSMENT.EXPECTS_ERROR_CATEGORIES.includes(category)) {
622
- return 'error';
623
- }
624
- // Check if category expects success
625
- if (OUTCOME_ASSESSMENT.EXPECTS_SUCCESS_CATEGORIES.includes(category)) {
626
- return 'success';
627
- }
628
- // Check description patterns for error expectation
629
- for (const pattern of OUTCOME_ASSESSMENT.EXPECTS_ERROR_PATTERNS) {
630
- if (pattern.test(description)) {
631
- return 'error';
632
- }
633
- }
634
- // Default to 'either' for edge cases
635
- return 'either';
359
+ const generated = generateSmartNumberValueResult(prop, propName).value;
360
+ return typeof generated === 'number' ? generated : 1;
636
361
  }
637
362
  /**
638
363
  * Add a question to the list, avoiding duplicates.
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Test fixture pattern for matching parameter names.
3
+ */
4
+ export interface TestFixturePattern {
5
+ /** Regex pattern to match parameter names */
6
+ match: string;
7
+ /** Value to use for matching parameters */
8
+ value: unknown;
9
+ }
10
+ /**
11
+ * Test fixtures configuration for customizing generated test values.
12
+ */
13
+ export interface TestFixturesConfig {
14
+ /** Custom values for specific parameter names (exact match) */
15
+ parameterValues?: Record<string, unknown>;
16
+ /** Custom values for parameters matching regex patterns */
17
+ patterns?: TestFixturePattern[];
18
+ }
19
+ //# sourceMappingURL=test-fixtures.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=test-fixtures.js.map