1515// only, and listed the saved one after a restart. The service now reads the
1616// engine registry, resolved when validation runs.
1717//
18- // ## What this file pins, and what it leaves to the seam pins
18+ // ## What this file pins
1919//
20- // The listing, through the doors an operator uses: the code-defined objects,
21- // an object saved through `PUT /meta/object/:name`, and an object imported
22- // through `…/external/tables/:remote/import`. Under this harness the
23- // federation service is mounted as an extra plugin and finds no `metadata`
24- // service at its `init()`, so it holds no datasource definition and every row
25- // it answers is `ok` with no comparison made. The VERDICT on a runtime-saved
26- // object is therefore pinned beside the plugin
27- // (`packages/services/service-datasource/src/__tests__/external-validate-reads-live-registry.test.ts`),
28- // and was measured on `objectstack dev`, where the definition is read.
20+ // Through the doors an operator uses: the code-defined objects are listed;
21+ // an object saved through `PUT /meta/object/:name` is listed with a real
22+ // verdict (it declares a column the remote lacks, and exactly that column is
23+ // reported); an object imported through `…/external/tables/:remote/import` is
24+ // listed beside them. The verify harness composes no metadata plugin, so its
25+ // `metadata` service is the kernel's fallback registered after the
26+ // federation service's `init()`, as on `objectstack start`; the service reads
27+ // it when validation runs ([#21876]), so each row here is a real comparison.
28+ // The seam pins sit beside the plugin
29+ // (`packages/services/service-datasource/src/__tests__/external-validate-reads-live-registry.test.ts`).
2930//
3031// The working directory is a temporary one because the showcase's external
3132// datasource and its fixture both name a cwd-relative SQLite file: this
@@ -45,16 +46,30 @@ const DATASOURCE = 'showcase_external';
4546const CODE_DEFINED = [ 'showcase_ext_customer' , 'showcase_ext_order' ] ;
4647/** Saved at runtime through the metadata door, bound to the remote `customers` table. */
4748const SAVED = 'dogfood_ext_cust_21842' ;
49+ /** A column the saved object declares and the remote `customers` table does not have. */
50+ const MISSING = 'loyalty_tier' ;
4851/** Imported at runtime from the remote `orders` table. */
4952const IMPORTED = 'dogfood_ext_ord_21842' ;
5053
5154const validatePath = `/datasources/${ DATASOURCE } /external/validate` ;
5255
56+ interface Row {
57+ object : string ;
58+ ok : boolean ;
59+ diffs : Array < { kind : string ; column ?: string ; severity : string } > ;
60+ }
61+
62+ function rowsOf ( json : unknown ) : Row [ ] {
63+ const body = json as { data ?: { results ?: Row [ ] } } | undefined ;
64+ return body ?. data ?. results ?? [ ] ;
65+ }
66+
5367function listedObjects ( json : unknown ) : string [ ] {
54- const body = json as { data ?: { results ?: Array < { object ?: unknown } > } } | undefined ;
55- return ( body ?. data ?. results ?? [ ] ) . map ( ( r ) => String ( r . object ) ) . sort ( ) ;
68+ return rowsOf ( json ) . map ( ( r ) => String ( r . object ) ) . sort ( ) ;
5669}
5770
71+ const rowOf = ( json : unknown , object : string ) => rowsOf ( json ) . find ( ( r ) => r . object === object ) ;
72+
5873describe ( 'external validate lists a federated object saved at runtime, with no restart (showcase)' , ( ) => {
5974 let stack : VerifyStack ;
6075 let token : string ;
@@ -94,20 +109,30 @@ describe('external validate lists a federated object saved at runtime, with no r
94109 expect ( listedObjects ( validated . json ) ) . toEqual ( CODE_DEFINED ) ;
95110 } ) ;
96111
97- it ( 'lists an object saved through PUT /meta/object/:name, and still the code-defined ones' , async ( ) => {
112+ it ( 'lists an object saved through PUT /meta/object/:name with a real verdict , and still the code-defined ones' , async ( ) => {
98113 const saved = await call ( 'PUT' , `/meta/object/${ SAVED } ` , {
99114 name : SAVED ,
100115 label : 'Dogfood External Customer' ,
101116 sharingModel : 'public_read_write' ,
102117 datasource : DATASOURCE ,
103118 external : { remoteName : 'customers' } ,
104- fields : { name : { type : 'text' , label : 'Name' } , email : { type : 'text' , label : 'Email' } } ,
119+ fields : {
120+ name : { type : 'text' , label : 'Name' } ,
121+ email : { type : 'text' , label : 'Email' } ,
122+ [ MISSING ] : { type : 'text' , label : 'Loyalty Tier' } ,
123+ } ,
105124 } ) ;
106125 expect ( saved . status , JSON . stringify ( saved . json ) ) . toBe ( 200 ) ;
107126
108127 const validated = await call ( 'POST' , validatePath ) ;
109128 expect ( validated . status , JSON . stringify ( validated . json ) ) . toBe ( 200 ) ;
110129 expect ( listedObjects ( validated . json ) ) . toEqual ( [ ...CODE_DEFINED , SAVED ] . sort ( ) ) ;
130+ // Compared, not waved through: exactly the declared column the remote lacks.
131+ expect ( rowOf ( validated . json , SAVED ) ) . toMatchObject ( {
132+ ok : false ,
133+ diffs : [ { kind : 'missing_column' , remoteName : 'customers' , column : MISSING , severity : 'error' } ] ,
134+ } ) ;
135+ for ( const name of CODE_DEFINED ) expect ( rowOf ( validated . json , name ) ?. ok , name ) . toBe ( true ) ;
111136 } ) ;
112137
113138 it ( 'lists an object imported from a remote table, beside the saved and code-defined ones' , async ( ) => {
@@ -117,5 +142,9 @@ describe('external validate lists a federated object saved at runtime, with no r
117142 const validated = await call ( 'POST' , validatePath ) ;
118143 expect ( validated . status , JSON . stringify ( validated . json ) ) . toBe ( 200 ) ;
119144 expect ( listedObjects ( validated . json ) ) . toEqual ( [ ...CODE_DEFINED , IMPORTED , SAVED ] . sort ( ) ) ;
145+ expect ( rowOf ( validated . json , IMPORTED ) , JSON . stringify ( rowOf ( validated . json , IMPORTED ) ) ) . toMatchObject ( {
146+ ok : true ,
147+ diffs : [ ] ,
148+ } ) ;
120149 } ) ;
121150} ) ;
0 commit comments