@@ -14,7 +14,6 @@ import {
1414 matchOption ,
1515 splitMulti ,
1616 coerceRow ,
17- firstConstraintViolation ,
1817} from './import-coerce' ;
1918import type { ExportFieldMeta } from './export-format' ;
2019
@@ -274,76 +273,23 @@ describe('coerceRow', () => {
274273 } ) ;
275274} ) ;
276275
277- describe ( 'firstConstraintViolation (framework#3956)' , ( ) => {
278- const meta = ( defs : Record < string , Partial < ExportFieldMeta > > ) : Map < string , ExportFieldMeta > => {
279- const m = new Map < string , ExportFieldMeta > ( ) ;
280- for ( const [ name , d ] of Object . entries ( defs ) ) m . set ( name , { name, ...d } ) ;
281- return m ;
282- } ;
283-
284- it ( 'reports a numeric value below `min` with the engine\'s own message' , ( ) => {
285- // The issue's repro: penalty_amount { type: 'number', min: 0, max: 9999999.99 }
286- const metaMap = meta ( { penalty_amount : { type : 'number' , min : 0 , max : 9999999.99 } } ) ;
287- expect ( firstConstraintViolation ( { penalty_amount : - 500 } , metaMap ) ) . toEqual ( {
288- field : 'penalty_amount' , code : 'min_value' , message : 'penalty_amount must be ≥ 0' ,
289- } ) ;
290- } ) ;
291-
292- it ( 'reports a numeric value above `max`' , ( ) => {
293- const metaMap = meta ( { pct : { type : 'percent' , max : 100 } } ) ;
294- expect ( firstConstraintViolation ( { pct : 101 } , metaMap ) ) . toEqual ( {
295- field : 'pct' , code : 'max_value' , message : 'pct must be ≤ 100' ,
296- } ) ;
297- } ) ;
298-
299- it ( 'reports string length violations both ways' , ( ) => {
300- const metaMap = meta ( { code : { type : 'text' , minLength : 3 , maxLength : 5 } } ) ;
301- expect ( firstConstraintViolation ( { code : 'abcdef' } , metaMap ) ) . toEqual ( {
302- field : 'code' , code : 'max_length' , message : 'code must be ≤ 5 characters (got 6)' ,
303- } ) ;
304- expect ( firstConstraintViolation ( { code : 'ab' } , metaMap ) ) . toEqual ( {
305- field : 'code' , code : 'min_length' , message : 'code must be ≥ 3 characters (got 2)' ,
306- } ) ;
307- } ) ;
308-
309- it ( 'accepts values inside the declared bounds' , ( ) => {
310- const metaMap = meta ( {
311- amount : { type : 'currency' , min : 0 , max : 100 } ,
312- title : { type : 'text' , maxLength : 10 } ,
313- } ) ;
314- expect ( firstConstraintViolation ( { amount : 0 } , metaMap ) ) . toBeNull ( ) ;
315- expect ( firstConstraintViolation ( { amount : 100 } , metaMap ) ) . toBeNull ( ) ;
316- expect ( firstConstraintViolation ( { title : 'ten chars!' } , metaMap ) ) . toBeNull ( ) ;
317- } ) ;
318-
319- it ( 'skips absent values — a bound never fires on a field the row omits' , ( ) => {
320- const metaMap = meta ( { amount : { type : 'number' , min : 10 } } ) ;
321- expect ( firstConstraintViolation ( { } , metaMap ) ) . toBeNull ( ) ;
322- expect ( firstConstraintViolation ( { amount : null } , metaMap ) ) . toBeNull ( ) ;
323- expect ( firstConstraintViolation ( { amount : '' } , metaMap ) ) . toBeNull ( ) ;
324- } ) ;
325-
326- it ( 'skips system / readonly columns the importer never supplies' , ( ) => {
327- const metaMap = meta ( {
328- seq : { type : 'number' , min : 100 , system : true } ,
329- score : { type : 'number' , min : 100 , readonly : true } ,
330- } ) ;
331- expect ( firstConstraintViolation ( { seq : 1 , score : 1 } , metaMap ) ) . toBeNull ( ) ;
332- } ) ;
333-
334- it ( 'leaves an unparseable number to coerceRow rather than double-reporting' , ( ) => {
335- const metaMap = meta ( { amount : { type : 'number' , min : 0 } } ) ;
336- expect ( firstConstraintViolation ( { amount : 'abc' } , metaMap ) ) . toBeNull ( ) ;
337- } ) ;
338-
339- it ( 'bound-checks only the types the engine bound-checks' , ( ) => {
340- // `progress` is numeric per the spec but the engine's validateOne leaves it
341- // unchecked — mirroring the wider spec set here would reject rows the real
342- // write accepts.
343- const metaMap = meta ( { p : { type : 'progress' , min : 0 , max : 1 } } ) ;
344- expect ( firstConstraintViolation ( { p : 42 } , metaMap ) ) . toBeNull ( ) ;
345- } ) ;
346- } ) ;
276+ // ── the retired constraint mirror (framework#3956) ────────────────────
277+ //
278+ // `firstConstraintViolation` used to be pinned here with eight unit cases. It
279+ // is gone: the import dry run asks the engine for its verdict through
280+ // `DataProtocol.validateData` instead of re-deriving one (#4633 ruling D), so
281+ // there is no longer a copy of the engine's numeric-range / string-length
282+ // rules in this file to keep in step.
283+ //
284+ // Its VERDICTS did not retire with it — `import-dryrun-parity.test.ts` asserts
285+ // every one of them (min_value, max_value, min_length, max_length, an omitted
286+ // bounded field, boundary values) against a live engine, and asserts the dry
287+ // run and the real write agree on each. Two of the eight cases have no
288+ // successor by design: "skips system / readonly columns" and "bound-checks
289+ // only the types the engine bound-checks" existed because a hand-maintained
290+ // copy could disagree with the engine about WHICH fields and types are in
291+ // scope. With no copy, there is no second opinion to police — that question is
292+ // `record-validator.ts`'s alone, and pinned in objectql's own tests.
347293
348294/**
349295 * #3957 — the importer's row report is where a user meets these messages, and
0 commit comments