Fix coverage with several contract classes; table annotation and file path checks; clean task dir on write

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aUaQeLnwbb1zTpN7kHeat
This commit is contained in:
Kral
2026-10-03 08:44:35 +02:00
parent fb576b0954
commit 03ab36149e
388 changed files with 28620 additions and 339 deletions

View File

@@ -0,0 +1,32 @@
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Call charge'
define view entity {{P}}I_CALL_CHARGE
as select from {{p}}calls
{
key call_id as call_id,
customer as customer,
cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.int4 ) as billable_min,
cast(
case dest_type
when 'N' then 9
when 'I' then 25
else 16
end as abap.dec(5,2)
) / 100 as rate,
cast(
round(
cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.dec(15,2) )
* ( cast(
case dest_type
when 'N' then 9
when 'I' then 25
else 15
end as abap.dec(5,2)
) / 100 )
* case when duration_sec > 1800
then cast( 90 as abap.dec(5,2) ) / 100
else cast( 100 as abap.dec(5,2) ) / 100
end
, 2 )
as abap.dec(15,2) ) as charge
}

View File

@@ -0,0 +1,32 @@
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Call charge'
define view entity {{P}}I_CALL_CHARGE
as select from {{p}}calls
{
key call_id as call_id,
customer as customer,
cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.int4 ) as billable_min,
cast(
case dest_type
when 'N' then 9
when 'I' then 25
else 15
end as abap.dec(5,2)
) / 100 as rate,
cast(
round(
cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.dec(15,2) )
* ( cast(
case dest_type
when 'N' then 9
when 'Z' then 25
else 15
end as abap.dec(5,2)
) / 100 )
* case when duration_sec > 1800
then cast( 90 as abap.dec(5,2) ) / 100
else cast( 100 as abap.dec(5,2) ) / 100
end
, 2 )
as abap.dec(15,2) ) as charge
}

View File

@@ -0,0 +1,32 @@
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Call charge'
define view entity {{P}}I_CALL_CHARGE
as select from {{p}}calls
{
key call_id as call_id,
customer as customer,
cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.int4 ) as billable_min,
cast(
case dest_type
when 'N' then 9
when 'I' then 25
else 15
end as abap.dec(5,2)
) / 100 as rate,
cast(
round(
cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.dec(15,2) )
* ( cast(
case dest_type
when 'N' then 9
when 'I' then 26
else 15
end as abap.dec(5,2)
) / 100 )
* case when duration_sec > 1800
then cast( 90 as abap.dec(5,2) ) / 100
else cast( 100 as abap.dec(5,2) ) / 100
end
, 2 )
as abap.dec(15,2) ) as charge
}

View File

@@ -0,0 +1,32 @@
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Call charge'
define view entity {{P}}I_CALL_CHARGE
as select from {{p}}calls
{
key call_id as call_id,
customer as customer,
cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.int4 ) as billable_min,
cast(
case dest_type
when 'N' then 9
when 'I' then 25
else 15
end as abap.dec(5,2)
) / 100 as rate,
cast(
round(
cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.dec(15,2) )
* ( cast(
case dest_type
when 'N' then 9
when 'I' then 25
else 15
end as abap.dec(5,2)
) / 100 )
* case when duration_sec <= 1800
then cast( 90 as abap.dec(5,2) ) / 100
else cast( 100 as abap.dec(5,2) ) / 100
end
, 2 )
as abap.dec(15,2) ) as charge
}

View File

@@ -0,0 +1,34 @@
{
"id": "G0108",
"pool": "eval",
"object_type": "DDLS",
"category": "B",
"attempts": [
{
"stage": "validate",
"oracle": 65.0,
"null": 0
},
{
"stage": "validate",
"oracle": 65.0,
"null": 0
},
{
"stage": "validate",
"oracle": 98.5,
"null": 0
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 5,
"killed": 4,
"ok": true
}
}
],
"accepted": true
}

View File

@@ -0,0 +1,110 @@
CLASS {{p}}t16_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA go_env TYPE REF TO if_cds_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS setup.
METHODS assert_amount
IMPORTING iv_exp TYPE decfloat34
iv_act TYPE decfloat34.
METHODS billable_min_rounds_up FOR TESTING.
METHODS billable_min_exact FOR TESTING.
METHODS rate_national FOR TESTING.
METHODS rate_international FOR TESTING.
METHODS rate_other FOR TESTING.
METHODS charge_no_discount FOR TESTING.
METHODS charge_long_discount FOR TESTING.
METHODS charge_rounding FOR TESTING.
METHODS charge_boundary FOR TESTING.
METHODS row_count FOR TESTING.
ENDCLASS.
CLASS {{p}}t16_hidden IMPLEMENTATION.
METHOD class_setup.
go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_CALL_CHARGE' ).
ENDMETHOD.
METHOD class_teardown.
go_env->destroy( ).
ENDMETHOD.
METHOD setup.
DATA lt_calls TYPE STANDARD TABLE OF {{p}}calls WITH EMPTY KEY.
go_env->clear_doubles( ).
lt_calls = VALUE #(
( call_id = 'C1' customer = 'ACME' call_date = '20240110' start_time = '100000' duration_sec = 125 dest_type = 'N' )
( call_id = 'C2' customer = 'BETA' call_date = '20240110' start_time = '220000' duration_sec = 3600 dest_type = 'I' )
( call_id = 'C3' customer = 'GAMMA' call_date = '20240110' start_time = '073000' duration_sec = 30 dest_type = 'N' )
( call_id = 'C4' customer = 'DELTA' call_date = '20240110' start_time = '120000' duration_sec = 120 dest_type = 'X' )
( call_id = 'C5' customer = 'EPSILON' call_date = '20240110' start_time = '120000' duration_sec = 1800 dest_type = 'N' )
( call_id = 'C6' customer = 'ZETA' call_date = '20240110' start_time = '120000' duration_sec = 1801 dest_type = 'N' )
).
go_env->insert_test_data( lt_calls ).
ENDMETHOD.
METHOD assert_amount.
DATA lv_diff TYPE decfloat34.
lv_diff = abs( iv_exp - iv_act ).
cl_abap_unit_assert=>assert_equals(
exp = abap_true
act = xsdbool( lv_diff < CONV decfloat34( '0.005' ) ) ).
ENDMETHOD.
METHOD billable_min_rounds_up.
SELECT SINGLE billable_min FROM {{p}}i_call_charge WHERE call_id = 'C1' INTO @DATA(lv_min1).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lv_min1 ).
SELECT SINGLE billable_min FROM {{p}}i_call_charge WHERE call_id = 'C2' INTO @DATA(lv_min2).
cl_abap_unit_assert=>assert_equals( exp = 60 act = lv_min2 ).
SELECT SINGLE billable_min FROM {{p}}i_call_charge WHERE call_id = 'C3' INTO @DATA(lv_min3).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lv_min3 ).
SELECT SINGLE billable_min FROM {{p}}i_call_charge WHERE call_id = 'C4' INTO @DATA(lv_min4).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lv_min4 ).
ENDMETHOD.
METHOD billable_min_exact.
SELECT SINGLE billable_min FROM {{p}}i_call_charge WHERE call_id = 'C5' INTO @DATA(lv_min).
cl_abap_unit_assert=>assert_equals( exp = 30 act = lv_min ).
ENDMETHOD.
METHOD rate_national.
SELECT SINGLE rate FROM {{p}}i_call_charge WHERE call_id = 'C1' INTO @DATA(lv_rate).
assert_amount( iv_exp = CONV decfloat34( '0.09' ) iv_act = CONV decfloat34( lv_rate ) ).
ENDMETHOD.
METHOD rate_international.
SELECT SINGLE rate FROM {{p}}i_call_charge WHERE call_id = 'C2' INTO @DATA(lv_rate).
assert_amount( iv_exp = CONV decfloat34( '0.25' ) iv_act = CONV decfloat34( lv_rate ) ).
ENDMETHOD.
METHOD rate_other.
SELECT SINGLE rate FROM {{p}}i_call_charge WHERE call_id = 'C4' INTO @DATA(lv_rate).
assert_amount( iv_exp = CONV decfloat34( '0.15' ) iv_act = CONV decfloat34( lv_rate ) ).
ENDMETHOD.
METHOD charge_no_discount.
SELECT SINGLE charge FROM {{p}}i_call_charge WHERE call_id = 'C1' INTO @DATA(lv_charge).
assert_amount( iv_exp = CONV decfloat34( '0.27' ) iv_act = CONV decfloat34( lv_charge ) ).
ENDMETHOD.
METHOD charge_long_discount.
SELECT SINGLE charge FROM {{p}}i_call_charge WHERE call_id = 'C2' INTO @DATA(lv_charge).
assert_amount( iv_exp = CONV decfloat34( '13.50' ) iv_act = CONV decfloat34( lv_charge ) ).
ENDMETHOD.
METHOD charge_rounding.
SELECT SINGLE charge FROM {{p}}i_call_charge WHERE call_id = 'C6' INTO @DATA(lv_charge).
assert_amount( iv_exp = CONV decfloat34( '2.51' ) iv_act = CONV decfloat34( lv_charge ) ).
ENDMETHOD.
METHOD charge_boundary.
SELECT SINGLE charge FROM {{p}}i_call_charge WHERE call_id = 'C5' INTO @DATA(lv_charge).
assert_amount( iv_exp = CONV decfloat34( '2.70' ) iv_act = CONV decfloat34( lv_charge ) ).
ENDMETHOD.
METHOD row_count.
SELECT COUNT(*) FROM {{p}}i_call_charge INTO @DATA(lv_rows).
cl_abap_unit_assert=>assert_equals( exp = 6 act = lv_rows ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,55 @@
{
"task": "G0108",
"mutants": [
{
"object": "{{P}}I_CALL_CHARGE",
"mutant": "line 13: 15 -> 16 (const)",
"status": "killed",
"hidden": "9/10",
"failed_tests": [
"RATE_OTHER"
]
},
{
"object": "{{P}}I_CALL_CHARGE",
"mutant": "line 22: 'I' -> 'Z' (lit)",
"status": "killed",
"hidden": "9/10",
"failed_tests": [
"CHARGE_LONG_DISCOUNT"
]
},
{
"object": "{{P}}I_CALL_CHARGE",
"mutant": "line 22: 25 -> 26 (const)",
"status": "killed",
"hidden": "9/10",
"failed_tests": [
"CHARGE_LONG_DISCOUNT"
]
},
{
"object": "{{P}}I_CALL_CHARGE",
"mutant": "line 23: 15 -> 16 (const)",
"status": "survived",
"hidden": "10/10",
"failed_tests": []
},
{
"object": "{{P}}I_CALL_CHARGE",
"mutant": "line 26: > -> <= (rel)",
"status": "killed",
"hidden": "6/10",
"failed_tests": [
"CHARGE_BOUNDARY",
"CHARGE_LONG_DISCOUNT",
"CHARGE_NO_DISCOUNT",
"CHARGE_ROUNDING"
]
}
],
"valid": 5,
"killed": 4,
"kill_rate": 0.8,
"ok": true
}

View File

@@ -0,0 +1,32 @@
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Call charge'
define view entity {{P}}I_CALL_CHARGE
as select from {{p}}calls
{
key call_id as call_id,
customer as customer,
cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.int4 ) as billable_min,
cast(
case dest_type
when 'N' then 9
when 'I' then 25
else 15
end as abap.dec(5,2)
) / 100 as rate,
cast(
round(
cast( ceil( cast( duration_sec as abap.dec(15,2) ) / 60 ) as abap.dec(15,2) )
* ( cast(
case dest_type
when 'N' then 9
when 'I' then 25
else 15
end as abap.dec(5,2)
) / 100 )
* case when duration_sec > 1800
then cast( 90 as abap.dec(5,2) ) / 100
else cast( 100 as abap.dec(5,2) ) / 100
end
, 2 )
as abap.dec(15,2) ) as charge
}

View File

@@ -0,0 +1,52 @@
CLASS {{p}}t16_test DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA go_env TYPE REF TO if_cds_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS setup.
METHODS assert_amount
IMPORTING iv_exp TYPE decfloat34
iv_act TYPE decfloat34.
METHODS charge_of_national_call FOR TESTING.
METHODS charge_of_long_call FOR TESTING.
ENDCLASS.
CLASS {{p}}t16_test IMPLEMENTATION.
METHOD class_setup.
go_env = cl_cds_test_environment=>create( i_for_entity = '{{P}}I_CALL_CHARGE' ).
ENDMETHOD.
METHOD class_teardown.
go_env->destroy( ).
ENDMETHOD.
METHOD setup.
DATA lt_calls TYPE STANDARD TABLE OF {{p}}calls WITH EMPTY KEY.
go_env->clear_doubles( ).
lt_calls = VALUE #(
( call_id = 'A' customer = 'C1' call_date = '20240110' start_time = '100000' duration_sec = 60 dest_type = 'N' )
( call_id = 'B' customer = 'C2' call_date = '20240110' start_time = '100000' duration_sec = 3600 dest_type = 'I' )
).
go_env->insert_test_data( lt_calls ).
ENDMETHOD.
METHOD assert_amount.
DATA lv_diff TYPE decfloat34.
lv_diff = abs( iv_exp - iv_act ).
cl_abap_unit_assert=>assert_equals(
exp = abap_true
act = xsdbool( lv_diff < CONV decfloat34( '0.005' ) ) ).
ENDMETHOD.
METHOD charge_of_national_call.
SELECT SINGLE charge FROM {{p}}i_call_charge WHERE call_id = 'A' INTO @DATA(lv_charge).
assert_amount( iv_exp = CONV decfloat34( '0.09' ) iv_act = CONV decfloat34( lv_charge ) ).
ENDMETHOD.
METHOD charge_of_long_call.
SELECT SINGLE charge FROM {{p}}i_call_charge WHERE call_id = 'B' INTO @DATA(lv_charge).
assert_amount( iv_exp = CONV decfloat34( '13.50' ) iv_act = CONV decfloat34( lv_charge ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,14 @@
@EndUserText.label : 'Phone call'
@AbapCatalog.enhancement.category : #NOT_EXTENSIBLE
@AbapCatalog.tableCategory : #TRANSPARENT
@AbapCatalog.deliveryClass : #A
@AbapCatalog.dataMaintenance : #RESTRICTED
define table {{p}}calls {
key client : abap.clnt not null;
key call_id : abap.char(10) not null;
customer : abap.char(10);
call_date : abap.dats;
start_time : abap.tims;
duration_sec : abap.int4;
dest_type : abap.char(1);
}

View File

@@ -0,0 +1,37 @@
# 1. Goal
Give the charge of each phone call. A billing program will read the charges, so the
calculation must be in the database, in a CDS view.
# 2. Open questions
None.
# 3. Context
- Table {{P}}CALLS (phone calls), package $TMP. Key: CALL_ID (CHAR 10).
Fields: CUSTOMER (CHAR 10), CALL_DATE (DATS), START_TIME (TIMS), DURATION_SEC (INT4),
DEST_TYPE (CHAR 1).
# 4. Contract
- Create the CDS view entity {{P}}I_CALL_CHARGE in package $TMP.
- Elements, with these names: CALL_ID (key), CUSTOMER, BILLABLE_MIN, RATE, CHARGE.
- No authorization check (#NOT_REQUIRED).
# 5. Business rules
1. Show one row for each call.
2. BILLABLE_MIN is the duration in minutes, rounded up to the next full minute. A call
of 1 second counts as 1 minute.
3. RATE is the price per minute for the destination type:
- 'N' (national): 0.09
- 'I' (international): 0.25
- any other value: 0.15
4. Calls longer than 1800 seconds get a 10% discount on the charge.
5. CHARGE is BILLABLE_MIN multiplied by RATE, with the discount applied, rounded to 2
decimals.
# 6. Constraints
- Release target: 8.16.
- Out of scope: do not change {{P}}CALLS.
# 7. Acceptance
- The view is active and has no syntax error.
- The hidden tests pass.
- Write ABAP Unit tests with CL_CDS_TEST_ENVIRONMENT in a global test class.

View File

@@ -0,0 +1,59 @@
{
"id": "G0108",
"category": "B",
"object_type": "DDLS",
"difficulty": 2,
"release_target": "v816",
"expected_outcome": "implement",
"budget": {
"max_tool_calls": 40,
"max_activations": 10
},
"seed": [
{
"type": "TABL",
"name": "{{P}}CALLS",
"file": "seed/calls.tabl.asabap",
"description": "Phone calls"
}
],
"contract": [
{
"type": "DDLS",
"name": "{{P}}I_CALL_CHARGE",
"fields": [
"CALL_ID",
"CUSTOMER",
"BILLABLE_MIN",
"RATE",
"CHARGE"
]
}
],
"out_of_scope": [
"{{P}}CALLS"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}T16_HIDDEN",
"file": "hidden/t16_hidden.clas.abap",
"description": "T16 hidden tests"
}
],
"reference": [
{
"type": "DDLS",
"name": "{{P}}I_CALL_CHARGE",
"file": "reference/i_call_charge.ddls.asddls",
"description": "Call charge"
},
{
"type": "CLAS",
"name": "{{P}}T16_TEST",
"file": "reference/t16_test.clas.abap",
"description": "T16 own tests"
}
],
"craft_checks": []
}