88import { describe , it , expect , beforeAll , afterAll } from 'vitest' ;
99import { runProtocolTCK , ProtocolEndpoint , ProtocolOperation , ProtocolResponse } from '@objectql/protocol-tck' ;
1010import { ODataV4Plugin } from './index' ;
11- import { ObjectKernel } from '@objectstack/core' ;
1211import { MemoryDriver } from '@objectql/driver-memory' ;
1312
1413/**
@@ -18,10 +17,10 @@ import { MemoryDriver } from '@objectql/driver-memory';
1817 */
1918class ODataEndpoint implements ProtocolEndpoint {
2019 private plugin : ODataV4Plugin ;
21- private kernel : ObjectKernel ;
20+ private kernel : any ;
2221 private baseUrl : string ;
2322
24- constructor ( plugin : ODataV4Plugin , kernel : ObjectKernel ) {
23+ constructor ( plugin : ODataV4Plugin , kernel : any ) {
2524 this . plugin = plugin ;
2625 this . kernel = kernel ;
2726 const port = plugin . config . port || 3000 ;
@@ -229,12 +228,20 @@ class ODataEndpoint implements ProtocolEndpoint {
229228 } ) ;
230229
231230 if ( ! response . ok ) {
232- const error = await response . json ( ) ;
231+ let errorMessage = 'Query failed' ;
232+ let errorCode = 'QUERY_ERROR' ;
233+ try {
234+ const error = await response . json ( ) ;
235+ errorCode = error . error ?. code || 'QUERY_ERROR' ;
236+ errorMessage = error . error ?. message || 'Query failed' ;
237+ } catch ( e ) {
238+ errorMessage = `HTTP ${ response . status } : ${ await response . text ( ) . catch ( ( ) => 'No error message' ) } ` ;
239+ }
233240 return {
234241 success : false ,
235242 error : {
236- code : error . error ?. code || 'QUERY_ERROR' ,
237- message : error . error ?. message || 'Query failed'
243+ code : errorCode ,
244+ message : errorMessage
238245 }
239246 } ;
240247 }
@@ -315,7 +322,20 @@ class ODataEndpoint implements ProtocolEndpoint {
315322 } ) ;
316323
317324 const metadata = await response . text ( ) ;
318- return { metadata, format : 'EDMX' } ;
325+
326+ // Extract entity sets from the EDMX XML for TCK compatibility
327+ // The TCK expects { entities } or { entitySets } or { types }
328+ const entitySetMatches = metadata . match ( / < E n t i t y S e t N a m e = " ( [ ^ " ] + ) " / g) || [ ] ;
329+ const entitySets = entitySetMatches . map ( match => {
330+ const nameMatch = match . match ( / N a m e = " ( [ ^ " ] + ) " / ) ;
331+ return nameMatch ? nameMatch [ 1 ] : '' ;
332+ } ) . filter ( Boolean ) ;
333+
334+ return {
335+ metadata,
336+ format : 'EDMX' ,
337+ entitySets // Add this for TCK compatibility
338+ } ;
319339 }
320340
321341 async close ( ) : Promise < void > {
@@ -373,45 +393,102 @@ class ODataEndpoint implements ProtocolEndpoint {
373393 * OData V4 Protocol TCK Test Suite
374394 */
375395describe ( 'OData V4 Protocol TCK' , ( ) => {
376- let kernel : ObjectKernel ;
396+ let kernel : any ; // Mock kernel
377397 let plugin : ODataV4Plugin ;
378398 let testPort : number ;
399+ let driver : MemoryDriver ;
379400
380401 beforeAll ( async ( ) => {
381402 // Use a unique port for tests to avoid conflicts
382403 testPort = 9100 + Math . floor ( Math . random ( ) * 1000 ) ;
383404
405+ // Create driver
406+ driver = new MemoryDriver ( ) ;
407+
408+ // Create mock kernel similar to integration tests
409+ const metadataStore = new Map < string , any > ( ) ;
410+ // Use capitalized name for OData entity sets
411+ metadataStore . set ( 'Tck_test_entity' , {
412+ content : {
413+ name : 'Tck_test_entity' ,
414+ label : 'TCK Test Entity' ,
415+ fields : {
416+ name : { type : 'text' , label : 'Name' } ,
417+ value : { type : 'number' , label : 'Value' } ,
418+ active : { type : 'boolean' , label : 'Active' }
419+ }
420+ }
421+ } ) ;
422+
423+ kernel = {
424+ metadata : {
425+ register : ( type : string , name : string , item : any ) => {
426+ metadataStore . set ( name , { content : item } ) ;
427+ } ,
428+ list : ( type : string ) => {
429+ if ( type === 'object' ) {
430+ return Array . from ( metadataStore . values ( ) ) ;
431+ }
432+ return [ ] ;
433+ } ,
434+ get : ( type : string , name : string ) => {
435+ return metadataStore . get ( name ) || null ;
436+ }
437+ } ,
438+ repository : {
439+ find : async ( objectName : string , query : any ) => driver . find ( objectName , query ) ,
440+ findOne : async ( objectName : string , id : string ) => driver . findOne ( objectName , id ) ,
441+ create : async ( objectName : string , data : any ) => driver . create ( objectName , data ) ,
442+ update : async ( objectName : string , id : string , data : any ) => driver . update ( objectName , id , data ) ,
443+ delete : async ( objectName : string , id : string ) => driver . delete ( objectName , id ) ,
444+ count : async ( objectName : string , filters : any ) => driver . count ( objectName , filters ) ,
445+ } ,
446+ // Add direct methods that OData plugin expects
447+ // Note: OData uses capitalized entity set names but driver uses lowercase
448+ find : async ( objectName : string , query : any ) => {
449+ const lowerName = objectName . toLowerCase ( ) ;
450+ return driver . find ( lowerName , query ) ;
451+ } ,
452+ get : async ( objectName : string , id : string ) => {
453+ const lowerName = objectName . toLowerCase ( ) ;
454+ return driver . findOne ( lowerName , id ) ;
455+ } ,
456+ create : async ( objectName : string , data : any ) => {
457+ const lowerName = objectName . toLowerCase ( ) ;
458+ return driver . create ( lowerName , data ) ;
459+ } ,
460+ update : async ( objectName : string , id : string , data : any ) => {
461+ const lowerName = objectName . toLowerCase ( ) ;
462+ return driver . update ( lowerName , id , data ) ;
463+ } ,
464+ delete : async ( objectName : string , id : string ) => {
465+ const lowerName = objectName . toLowerCase ( ) ;
466+ return driver . delete ( lowerName , id ) ;
467+ } ,
468+ count : async ( objectName : string , filters : any ) => {
469+ const lowerName = objectName . toLowerCase ( ) ;
470+ return driver . count ( lowerName , filters ) ;
471+ } ,
472+ driver,
473+ getDriver : ( ) => driver
474+ } ;
475+
384476 // Create test kernel with memory driver
385477 plugin = new ODataV4Plugin ( {
386478 port : testPort ,
387479 basePath : '/odata' ,
388480 namespace : 'TCKTest'
389481 } ) ;
390482
391- kernel = new ObjectKernel ( [
392- new MemoryDriver ( ) ,
393- plugin
394- ] ) ;
395-
396- // Register test entity
397- kernel . metadata . register ( 'object' , 'tck_test_entity' , {
398- name : 'tck_test_entity' ,
399- label : 'TCK Test Entity' ,
400- fields : {
401- name : { type : 'text' , label : 'Name' } ,
402- value : { type : 'number' , label : 'Value' } ,
403- active : { type : 'boolean' , label : 'Active' }
404- }
405- } ) ;
406-
407- await kernel . start ( ) ;
483+ await plugin . install ?.( { engine : kernel } ) ;
484+ await plugin . onStart ?.( { engine : kernel } ) ;
408485
409486 // Wait for server to be ready
410487 await new Promise ( resolve => setTimeout ( resolve , 1000 ) ) ;
411488 } , 30000 ) ;
412489
413490 afterAll ( async ( ) => {
414- await kernel . stop ( ) ;
491+ await plugin . onStop ?. ( { engine : kernel } ) ;
415492 } , 30000 ) ;
416493
417494 // Run the Protocol TCK
@@ -425,15 +502,6 @@ describe('OData V4 Protocol TCK', () => {
425502 federation : true // OData doesn't support GraphQL federation
426503 } ,
427504 timeout : 30000 ,
428- hooks : {
429- beforeEach : async ( ) => {
430- // Clear data between tests
431- const driver = kernel . getDriver ( ) ;
432- if ( driver && typeof ( driver as any ) . clear === 'function' ) {
433- await ( driver as any ) . clear ( ) ;
434- }
435- }
436- } ,
437505 performance : {
438506 enabled : true ,
439507 thresholds : {
0 commit comments