feat(adt-contracts): implement RAP contracts for behavior definitions, DDLS, and generator#92
feat(adt-contracts): implement RAP contracts for behavior definitions, DDLS, and generator#92kilo-code-bot[bot] wants to merge 1 commit intomainfrom
Conversation
…, DDLS, and generator - Add Behavior Definition (BDEF) contract for /sap/bc/adt/rap/behavdeft - Add CDS View/Entity (DDLS) contract for /sap/bc/adt/ddl/ddls - Add RAP Generator workspace contract for /sap/bc/adt/rap/generator - Export all contracts from adt/index.ts - Add comprehensive contract tests Implements RAP contract support for: - Full CRUD operations (get, post, put, delete) - Lock/unlock operations for editing - Source code access (get/put) - Object structure operations All 26 contract definition tests pass.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (4 files)
Review Notes: The implementation adds RAP (RESTful ABAP Programming) contracts covering:
All contracts follow existing codebase patterns with proper CRUD operations, lock/unlock mechanisms, and source code access. The test coverage is comprehensive with 26 tests passing. No security vulnerabilities, runtime errors, or logic bugs detected. Reviewed by minimax-m2.5-20260211 · 163,229 tokens |
|
|
View your CI Pipeline Execution ↗ for commit f72f9cc
☁️ Nx Cloud last updated this comment at |
|
Refinery code review passed. All quality gates pass (26 tests, typecheck, lint, format). |
| /** | ||
| * RAP Contracts Integration Tests | ||
| * | ||
| * These tests verify that the RAP contracts are properly defined. | ||
| * All tests run using contract definitions without live system. | ||
| * | ||
| * Run with: bun test packages/adt-contracts/tests/adt/rap/rap.test.ts | ||
| */ | ||
|
|
||
| import { describe, it, expect } from 'vitest'; | ||
| import { adtContract } from '../../../src/adt'; | ||
|
|
||
| describe('RAP Contracts - Contract Definition Tests', () => { | ||
| describe('Behavior Definition (BDEF) Contract', () => { | ||
| it('should define GET contract for BDEF', () => { | ||
| const contract = adtContract.rap.behavdeft; | ||
|
|
||
| const descriptor = contract.get('zcl_test_behav'); | ||
|
|
||
| expect(descriptor.method).toBe('GET'); | ||
| expect(descriptor.path).toContain('/sap/bc/adt/rap/behavdeft/'); | ||
| expect(descriptor.path).toContain('zcl_test_behav'); | ||
| expect(descriptor.headers?.Accept).toContain( | ||
| 'application/vnd.sap.adt.rap.behavdeft', | ||
| ); | ||
| }); | ||
|
|
||
| it('should define POST contract for creating BDEF', () => { | ||
| const contract = adtContract.rap.behavdeft; | ||
|
|
||
| const descriptor = contract.post(); | ||
|
|
||
| expect(descriptor.method).toBe('POST'); | ||
| expect(descriptor.path).toBe('/sap/bc/adt/rap/behavdeft'); | ||
| expect(descriptor.headers?.['Content-Type']).toContain( | ||
| 'application/vnd.sap.adt.rap.behavdeft', | ||
| ); | ||
| }); | ||
|
|
||
| it('should define PUT contract for updating BDEF', () => { | ||
| const contract = adtContract.rap.behavdeft; | ||
|
|
||
| const descriptor = contract.put('zcl_test_behav', { | ||
| corrNr: 'DEVK900001', | ||
| lockHandle: 'abc123', | ||
| }); | ||
|
|
||
| expect(descriptor.method).toBe('PUT'); | ||
| expect(descriptor.path).toContain('zcl_test_behav'); | ||
| expect(descriptor.query?.corrNr).toBe('DEVK900001'); | ||
| expect(descriptor.query?.lockHandle).toBe('abc123'); | ||
| }); | ||
|
|
||
| it('should define DELETE contract for BDEF', () => { | ||
| const contract = adtContract.rap.behavdeft; | ||
|
|
||
| const descriptor = contract.delete('zcl_test_behav'); | ||
|
|
||
| expect(descriptor.method).toBe('DELETE'); | ||
| expect(descriptor.path).toContain('zcl_test_behav'); | ||
| }); | ||
|
|
||
| it('should define lock contract for BDEF', () => { | ||
| const contract = adtContract.rap.behavdeft; | ||
|
|
||
| const descriptor = contract.lock('zcl_test_behav'); | ||
|
|
||
| expect(descriptor.method).toBe('POST'); | ||
| expect(descriptor.path).toContain('zcl_test_behav'); | ||
| expect(descriptor.query?._action).toBe('LOCK'); | ||
| }); | ||
|
|
||
| it('should define unlock contract for BDEF', () => { | ||
| const contract = adtContract.rap.behavdeft; | ||
|
|
||
| const descriptor = contract.unlock('zcl_test_behav', { | ||
| lockHandle: 'abc123', | ||
| }); | ||
|
|
||
| expect(descriptor.method).toBe('POST'); | ||
| expect(descriptor.path).toContain('zcl_test_behav'); | ||
| expect(descriptor.query?._action).toBe('UNLOCK'); | ||
| expect(descriptor.query?.lockHandle).toBe('abc123'); | ||
| }); | ||
|
|
||
| it('should define source access contract', () => { | ||
| const contract = adtContract.rap.behavdeft; | ||
|
|
||
| const sourceDescriptor = contract.source.main.get('zcl_test_behav'); | ||
|
|
||
| expect(sourceDescriptor.method).toBe('GET'); | ||
| expect(sourceDescriptor.path).toContain('/sap/bc/adt/rap/behavdeft/'); | ||
| expect(sourceDescriptor.path).toContain('/source/main'); | ||
| }); | ||
|
|
||
| it('should define objectstructure contract for BDEF', () => { | ||
| const contract = adtContract.rap.behavdeft; | ||
|
|
||
| const structureDescriptor = contract.objectstructure('zcl_test_behav'); | ||
|
|
||
| expect(structureDescriptor.method).toBe('GET'); | ||
| expect(structureDescriptor.path).toContain('zcl_test_behav'); | ||
| expect(structureDescriptor.path).toContain('/objectstructure'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('CDS View/Entity (DDLS) Contract', () => { | ||
| it('should define GET contract for DDLS', () => { | ||
| const contract = adtContract.rap.ddls; | ||
|
|
||
| const descriptor = contract.get('zcds_view'); | ||
|
|
||
| expect(descriptor.method).toBe('GET'); | ||
| expect(descriptor.path).toContain('/sap/bc/adt/ddl/ddls/'); | ||
| expect(descriptor.path).toContain('zcds_view'); | ||
| expect(descriptor.headers?.Accept).toContain( | ||
| 'application/vnd.sap.adt.ddl.ddlsource', | ||
| ); | ||
| }); | ||
|
|
||
| it('should define POST contract for DDLS', () => { | ||
| const contract = adtContract.rap.ddls; | ||
|
|
||
| const createDescriptor = contract.post(); | ||
|
|
||
| expect(createDescriptor.method).toBe('POST'); | ||
| expect(createDescriptor.path).toBe('/sap/bc/adt/ddl/ddls'); | ||
| expect(createDescriptor.headers?.['Content-Type']).toContain( | ||
| 'application/vnd.sap.adt.ddl.ddlsource', | ||
| ); | ||
| }); | ||
|
|
||
| it('should define PUT contract for DDLS', () => { | ||
| const contract = adtContract.rap.ddls; | ||
|
|
||
| const descriptor = contract.put('zcds_view', { | ||
| corrNr: 'DEVK900001', | ||
| lockHandle: 'abc123', | ||
| }); | ||
|
|
||
| expect(descriptor.method).toBe('PUT'); | ||
| expect(descriptor.path).toContain('zcds_view'); | ||
| expect(descriptor.query?.corrNr).toBe('DEVK900001'); | ||
| expect(descriptor.query?.lockHandle).toBe('abc123'); | ||
| }); | ||
|
|
||
| it('should define DELETE contract for DDLS', () => { | ||
| const contract = adtContract.rap.ddls; | ||
|
|
||
| const descriptor = contract.delete('zcds_view'); | ||
|
|
||
| expect(descriptor.method).toBe('DELETE'); | ||
| expect(descriptor.path).toContain('zcds_view'); | ||
| }); | ||
|
|
||
| it('should define lock contract for DDLS', () => { | ||
| const contract = adtContract.rap.ddls; | ||
|
|
||
| const descriptor = contract.lock('zcds_view', { | ||
| corrNr: 'DEVK900001', | ||
| }); | ||
|
|
||
| expect(descriptor.method).toBe('POST'); | ||
| expect(descriptor.path).toContain('zcds_view'); | ||
| expect(descriptor.query?._action).toBe('LOCK'); | ||
| }); | ||
|
|
||
| it('should define source access for DDLS', () => { | ||
| const contract = adtContract.rap.ddls; | ||
|
|
||
| const sourceDescriptor = contract.source.main.get('zcds_view'); | ||
|
|
||
| expect(sourceDescriptor.method).toBe('GET'); | ||
| expect(sourceDescriptor.path).toContain('/sap/bc/adt/ddl/ddls/'); | ||
| expect(sourceDescriptor.path).toContain('/source/main'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('RAP Generator Workspace Contract', () => { | ||
| it('should define list contract', () => { | ||
| const contract = adtContract.rap.generator; | ||
|
|
||
| const descriptor = contract.list(); | ||
|
|
||
| expect(descriptor.method).toBe('GET'); | ||
| expect(descriptor.path).toBe('/sap/bc/adt/rap/generator'); | ||
| expect(descriptor.headers?.Accept).toContain( | ||
| 'application/vnd.sap.adt.rap.generator', | ||
| ); | ||
| }); | ||
|
|
||
| it('should define get contract', () => { | ||
| const contract = adtContract.rap.generator; | ||
|
|
||
| const descriptor = contract.get('zrap_workspace'); | ||
|
|
||
| expect(descriptor.method).toBe('GET'); | ||
| expect(descriptor.path).toContain('/sap/bc/adt/rap/generator/'); | ||
| expect(descriptor.path).toContain('zrap_workspace'); | ||
| }); | ||
|
|
||
| it('should define create contract', () => { | ||
| const contract = adtContract.rap.generator; | ||
|
|
||
| const descriptor = contract.create({ corrNr: 'DEVK900001' }); | ||
|
|
||
| expect(descriptor.method).toBe('POST'); | ||
| expect(descriptor.path).toBe('/sap/bc/adt/rap/generator'); | ||
| expect(descriptor.query?.corrNr).toBe('DEVK900001'); | ||
| }); | ||
|
|
||
| it('should define update contract', () => { | ||
| const contract = adtContract.rap.generator; | ||
|
|
||
| const descriptor = contract.update('zrap_workspace', { | ||
| corrNr: 'DEVK900001', | ||
| lockHandle: 'abc123', | ||
| }); | ||
|
|
||
| expect(descriptor.method).toBe('PUT'); | ||
| expect(descriptor.path).toContain('zrap_workspace'); | ||
| expect(descriptor.query?.corrNr).toBe('DEVK900001'); | ||
| expect(descriptor.query?.lockHandle).toBe('abc123'); | ||
| }); | ||
|
|
||
| it('should define delete contract', () => { | ||
| const contract = adtContract.rap.generator; | ||
|
|
||
| const descriptor = contract.delete('zrap_workspace'); | ||
|
|
||
| expect(descriptor.method).toBe('DELETE'); | ||
| expect(descriptor.path).toContain('zrap_workspace'); | ||
| }); | ||
|
|
||
| it('should define lock contract', () => { | ||
| const contract = adtContract.rap.generator; | ||
|
|
||
| const descriptor = contract.lock('zrap_workspace', { | ||
| corrNr: 'DEVK900001', | ||
| }); | ||
|
|
||
| expect(descriptor.method).toBe('POST'); | ||
| expect(descriptor.path).toContain('zrap_workspace'); | ||
| expect(descriptor.query?._action).toBe('LOCK'); | ||
| expect(descriptor.query?.accessMode).toBe('MODIFY'); | ||
| }); | ||
|
|
||
| it('should define unlock contract', () => { | ||
| const contract = adtContract.rap.generator; | ||
|
|
||
| const descriptor = contract.unlock('zrap_workspace', 'lock-handle-123'); | ||
|
|
||
| expect(descriptor.method).toBe('POST'); | ||
| expect(descriptor.path).toContain('zrap_workspace'); | ||
| expect(descriptor.query?._action).toBe('UNLOCK'); | ||
| expect(descriptor.query?.lockHandle).toBe('lock-handle-123'); | ||
| }); | ||
|
|
||
| it('should define getSource contract', () => { | ||
| const contract = adtContract.rap.generator; | ||
|
|
||
| const descriptor = contract.getSource('zrap_workspace'); | ||
|
|
||
| expect(descriptor.method).toBe('GET'); | ||
| expect(descriptor.path).toContain('/source/main'); | ||
| expect(descriptor.headers?.Accept).toContain('text/plain'); | ||
| }); | ||
|
|
||
| it('should define putSource contract', () => { | ||
| const contract = adtContract.rap.generator; | ||
|
|
||
| const descriptor = contract.putSource('zrap_workspace', { | ||
| lockHandle: 'abc123', | ||
| }); | ||
|
|
||
| expect(descriptor.method).toBe('PUT'); | ||
| expect(descriptor.path).toContain('/source/main'); | ||
| expect(descriptor.headers?.['Content-Type']).toContain('text/plain'); | ||
| expect(descriptor.query?.lockHandle).toBe('abc123'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('RAP Contract Types', () => { | ||
| it('should export BehavdeftContract type', () => { | ||
| const contract = adtContract.rap.behavdeft; | ||
| expect(contract).toBeDefined(); | ||
| expect(typeof contract.get).toBe('function'); | ||
| expect(typeof contract.post).toBe('function'); | ||
| expect(typeof contract.put).toBe('function'); | ||
| expect(typeof contract.delete).toBe('function'); | ||
| }); | ||
|
|
||
| it('should export DdlsContract type', () => { | ||
| const contract = adtContract.rap.ddls; | ||
| expect(contract).toBeDefined(); | ||
| expect(typeof contract.get).toBe('function'); | ||
| expect(typeof contract.post).toBe('function'); | ||
| expect(typeof contract.put).toBe('function'); | ||
| expect(typeof contract.delete).toBe('function'); | ||
| }); | ||
|
|
||
| it('should export GeneratorContract type', () => { | ||
| const contract = adtContract.rap.generator; | ||
| expect(contract).toBeDefined(); | ||
| expect(typeof contract.list).toBe('function'); | ||
| expect(typeof contract.get).toBe('function'); | ||
| expect(typeof contract.create).toBe('function'); | ||
| }); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
🔴 Test file violates AGENTS.md convention: wrong location and missing ContractScenario/runScenario pattern
The packages/adt-contracts/AGENTS.md explicitly requires new contract tests to follow a specific structure:
- Create scenario in
tests/contracts/{area}.ts- Register in
tests/contracts/index.ts
The RAP tests are placed in tests/adt/rap/rap.test.ts instead of tests/contracts/rap.test.ts, and use ad-hoc describe/it blocks instead of the established ContractScenario/runScenario pattern used by every other contract test file (packages/adt-contracts/tests/contracts/oo.test.ts, packages/adt-contracts/tests/contracts/cts.test.ts, packages/adt-contracts/tests/contracts/system.test.ts, etc.). The ContractScenario base class at packages/adt-contracts/tests/contracts/base/index.ts provides standardized validation of method, path, headers, query, body schema, response schema, and fixture round-trips — none of which is leveraged by the new tests.
Prompt for agents
The test file at packages/adt-contracts/tests/adt/rap/rap.test.ts violates the contract testing convention documented in packages/adt-contracts/AGENTS.md. All contract tests must:
1. Be placed in tests/contracts/{area}.test.ts (e.g., tests/contracts/rap.test.ts)
2. Use the ContractScenario class and runScenario() function from tests/contracts/base/
3. Define operations as ContractOperation[] arrays with method, path, headers, query, body, and response fields
See tests/contracts/oo.test.ts or tests/contracts/system.test.ts for examples. The test should define scenario classes like BehavdeftScenario, DdlsScenario, and GeneratorScenario, each extending ContractScenario with the appropriate ContractOperation[] definitions, and then call runScenario() for each. Delete the tests/adt/rap/ directory entirely.
Was this helpful? React with 👍 or 👎 to provide feedback.
|
Duplicate of PR #93. Closing in favor of the other implementation. |



Summary
Changes
packages/adt-contracts/src/adt/rap/- New RAP contracts directorybehavdeft.ts- Behavior Definition contract for/sap/bc/adt/rap/behavdeftddls.ts- CDS View/Entity contract for/sap/bc/adt/ddl/ddlsgenerator.ts- RAP Generator workspace contract for/sap/bc/adt/rap/generatorindex.ts- Module exportspackages/adt-contracts/src/adt/index.ts- Updated to export RAP contractspackages/adt-contracts/tests/adt/rap/rap.test.ts- Contract testsRAP Contracts Features