Summary|
Tags|
Feature:
api/event-availability-visibility/event-availability-visibility.feature|
Ticketing MVP - Event Availability Visibility
As a buyer
I want to view event availability and tier status
So that I can see which tiers are available for purchase
Scenario: [1:12]
Scenario 1 - Event visible with valid availability by tier
ms: 100
>>
Background:
7
* def baseUrlEvents = karate.properties['baseUrlEvents'] || 'http://localhost:8081'
1
22:50:36.707 karate.env system property was: null
8
* def baseUrlTicketing = karate.properties['baseUrlTicketing'] || 'http://localhost:8082'
1
9
* def UUID = Java.type('java.util.UUID')
0
10
* def futureDate = '2026-12-15T20:00:00'
0
14
* def tag = UUID.randomUUID() + ''
1
15
* def eventTitle = 'Avail S1 ' + tag
0
17
Given url baseUrlEvents + '/api/v1/rooms'
0
18
And header X-Role = 'ADMIN'
0
19
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
20
And request { name: 'Availability Room S1', maxCapacity: 200 }
0
21
When method post
16
22:50:36.714 request:
1 > POST http://localhost:8081/api/v1/rooms
1 > X-Role: ADMIN
1 > X-User-Id: 00000000-0000-0000-0000-000000000001
1 > Content-Type: application/json; charset=UTF-8
1 > Content-Length: 49
1 > Host: localhost:8081
1 > Connection: Keep-Alive
1 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
1 > Accept-Encoding: gzip,deflate
{"name":"Availability Room S1","maxCapacity":200}
22:50:36.728 response time in milliseconds: 14
1 < 201
1 < Content-Type: application/json
1 < Transfer-Encoding: chunked
1 < Date: Wed, 08 Apr 2026 03:50:36 GMT
1 < Keep-Alive: timeout=60
1 < Connection: keep-alive
{"id":"6ebd1bc5-7f63-4ae3-abe2-b16d1a46a286","name":"Availability Room S1","maxCapacity":200,"created_at":"2026-04-08T03:50:36.717334123","updated_at":"2026-04-08T03:50:36.717351566"}
22
Then status 201
0
23
* def roomId = response.id
1
25
Given url baseUrlEvents + '/api/v1/events'
0
26
And header X-Role = 'ADMIN'
0
27
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
28
And request
1
{
"roomId": "#(roomId)",
"title": "#(eventTitle)",
"description": "Test event for availability",
"date": "#(futureDate)",
"capacity": 100,
"enableSeats": false
}
39
When method post
19
22:50:36.731 request:
2 > POST http://localhost:8081/api/v1/events
2 > X-Role: ADMIN
2 > X-User-Id: 00000000-0000-0000-0000-000000000001
2 > Content-Type: application/json; charset=UTF-8
2 > Content-Length: 213
2 > Host: localhost:8081
2 > Connection: Keep-Alive
2 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
2 > Accept-Encoding: gzip,deflate
{"roomId":"6ebd1bc5-7f63-4ae3-abe2-b16d1a46a286","title":"Avail S1 ec1d6ac6-17ab-4fb2-a33c-44b80005a763","description":"Test event for availability","date":"2026-12-15T20:00:00","capacity":100,"enableSeats":false}
22:50:36.749 response time in milliseconds: 17
2 < 201
2 < Content-Type: application/json
2 < Transfer-Encoding: chunked
2 < Date: Wed, 08 Apr 2026 03:50:36 GMT
2 < Keep-Alive: timeout=60
2 < Connection: keep-alive
{"id":"f8ba3e5f-7e05-4457-b9b0-39702f084b38","roomId":"6ebd1bc5-7f63-4ae3-abe2-b16d1a46a286","title":"Avail S1 ec1d6ac6-17ab-4fb2-a33c-44b80005a763","description":"Test event for availability","date":"2026-12-15T20:00:00","capacity":100,"status":"DRAFT","createdAt":"2026-04-08T03:50:36.738559018","updatedAt":"2026-04-08T03:50:36.738569769","createdBy":"00000000-0000-0000-0000-000000000001","imageUrl":null,"subtitle":null,"location":null,"director":null,"castMembers":null,"duration":null,"tag":null,"isLimited":false,"isFeatured":false,"enableSeats":false,"author":null}
40
Then status 201
0
41
* def eventId = response.id
1
43
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/tiers'
0
44
And header X-Role = 'ADMIN'
0
45
And request
0
[{ "tierType": "GENERAL", "price": 50, "quota": 100 }]
49
When method post
19
22:50:36.752 request:
3 > POST http://localhost:8081/api/v1/events/f8ba3e5f-7e05-4457-b9b0-39702f084b38/tiers
3 > X-Role: ADMIN
3 > Content-Type: application/json; charset=UTF-8
3 > Content-Length: 47
3 > Host: localhost:8081
3 > Connection: Keep-Alive
3 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
3 > Accept-Encoding: gzip,deflate
[{"tierType":"GENERAL","price":50,"quota":100}]
22:50:36.769 response time in milliseconds: 16
3 < 201
3 < Content-Type: application/json
3 < Transfer-Encoding: chunked
3 < Date: Wed, 08 Apr 2026 03:50:36 GMT
3 < Keep-Alive: timeout=60
3 < Connection: keep-alive
{"eventId":"f8ba3e5f-7e05-4457-b9b0-39702f084b38","tiers":[{"id":"2f5d0ddc-ea47-4e29-a64b-9e74b03c3d7c","tierType":"GENERAL","price":50,"quota":100,"validFrom":null,"validUntil":null,"createdAt":"2026-04-08T03:50:36.75999407","updatedAt":"2026-04-08T03:50:36.760006291"}]}
50
Then status 201
0
51
* def tierId = response.tiers[0].id
1
53
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/publish'
0
54
And header X-Role = 'ADMIN'
0
55
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
56
When method patch
22
22:50:36.771 request:
4 > PATCH http://localhost:8081/api/v1/events/f8ba3e5f-7e05-4457-b9b0-39702f084b38/publish
4 > X-Role: ADMIN
4 > X-User-Id: 00000000-0000-0000-0000-000000000001
4 > Host: localhost:8081
4 > Connection: Keep-Alive
4 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
4 > Accept-Encoding: gzip,deflate
22:50:36.793 response time in milliseconds: 20
4 < 200
4 < Content-Type: application/json
4 < Transfer-Encoding: chunked
4 < Date: Wed, 08 Apr 2026 03:50:36 GMT
4 < Keep-Alive: timeout=60
4 < Connection: keep-alive
{"id":"f8ba3e5f-7e05-4457-b9b0-39702f084b38","roomId":"6ebd1bc5-7f63-4ae3-abe2-b16d1a46a286","title":"Avail S1 ec1d6ac6-17ab-4fb2-a33c-44b80005a763","description":"Test event for availability","date":"2026-12-15T20:00:00","capacity":100,"status":"PUBLISHED","createdAt":"2026-04-08T03:50:36.738559","updatedAt":"2026-04-08T03:50:36.780636855","createdBy":"00000000-0000-0000-0000-000000000001","imageUrl":null,"subtitle":null,"location":null,"director":null,"castMembers":null,"duration":null,"tag":null,"isLimited":false,"isFeatured":false,"enableSeats":false,"author":null}
57
Then status 200
0
59
Given url baseUrlEvents + '/api/v1/events/' + eventId
0
60
When method get
14
22:50:36.794 request:
5 > GET http://localhost:8081/api/v1/events/f8ba3e5f-7e05-4457-b9b0-39702f084b38
5 > Host: localhost:8081
5 > Connection: Keep-Alive
5 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
5 > Accept-Encoding: gzip,deflate
22:50:36.807 response time in milliseconds: 13
5 < 200
5 < Content-Type: application/json
5 < Transfer-Encoding: chunked
5 < Date: Wed, 08 Apr 2026 03:50:36 GMT
5 < Keep-Alive: timeout=60
5 < Connection: keep-alive
{"id":"f8ba3e5f-7e05-4457-b9b0-39702f084b38","title":"Avail S1 ec1d6ac6-17ab-4fb2-a33c-44b80005a763","description":"Test event for availability","date":"2026-12-15T20:00:00","capacity":100,"room":{"id":"6ebd1bc5-7f63-4ae3-abe2-b16d1a46a286","name":"Availability Room S1","maxCapacity":200,"created_at":"2026-04-08T03:50:36.717334","updated_at":"2026-04-08T03:50:36.717352"},"availableTiers":[{"id":"2f5d0ddc-ea47-4e29-a64b-9e74b03c3d7c","tierType":"GENERAL","price":50.00,"quota":100,"validFrom":null,"validUntil":null,"isAvailable":true,"reason":null}],"created_at":"2026-04-08T03:50:36.738559","imageUrl":null,"subtitle":null,"location":null,"director":null,"castMembers":null,"duration":null,"tag":null,"isLimited":false,"isFeatured":false,"enableSeats":false,"author":null}
61
Then status 200
0
62
* match response.availableTiers != null && response.availableTiers.length > 0
1
63
* def tier = response.availableTiers[0]
0
64
* match tier.tierType == 'GENERAL'
0
65
* match tier.quota == 100
1
66
* match tier.isAvailable == true
0
67
* match tier.price == 50.0
0
68
* print 'Scenario 1 PASS: Event visible with 100 available in GENERAL tier'
0
22:50:36.811 [print] Scenario 1 PASS: Event visible with 100 available in GENERAL tier
Scenario: [2:71]
Scenario 2 - Exhausted tier (quota=2 reached via 2 approved payments)
ms: 851
>>
Background:
7
* def baseUrlEvents = karate.properties['baseUrlEvents'] || 'http://localhost:8081'
0
22:50:36.813 karate.env system property was: null
8
* def baseUrlTicketing = karate.properties['baseUrlTicketing'] || 'http://localhost:8082'
0
9
* def UUID = Java.type('java.util.UUID')
0
10
* def futureDate = '2026-12-15T20:00:00'
0
73
* def tag = UUID.randomUUID() + ''
0
74
* def eventTitle = 'Avail S2 ' + tag
0
75
* def buyer1Id = UUID.randomUUID() + ''
0
76
* def buyer2Id = UUID.randomUUID() + ''
0
78
Given url baseUrlEvents + '/api/v1/rooms'
0
79
And header X-Role = 'ADMIN'
0
80
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
81
And request { name: 'Exhausted Tier Room S2', maxCapacity: 50 }
0
82
When method post
12
22:50:36.817 request:
1 > POST http://localhost:8081/api/v1/rooms
1 > X-Role: ADMIN
1 > X-User-Id: 00000000-0000-0000-0000-000000000001
1 > Content-Type: application/json; charset=UTF-8
1 > Content-Length: 50
1 > Host: localhost:8081
1 > Connection: Keep-Alive
1 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
1 > Accept-Encoding: gzip,deflate
{"name":"Exhausted Tier Room S2","maxCapacity":50}
22:50:36.827 response time in milliseconds: 10
1 < 201
1 < Content-Type: application/json
1 < Transfer-Encoding: chunked
1 < Date: Wed, 08 Apr 2026 03:50:36 GMT
1 < Keep-Alive: timeout=60
1 < Connection: keep-alive
{"id":"10e2c018-89bf-4a37-b8e7-2bc016a4a856","name":"Exhausted Tier Room S2","maxCapacity":50,"created_at":"2026-04-08T03:50:36.820135714","updated_at":"2026-04-08T03:50:36.820148002"}
83
Then status 201
0
84
* def roomId = response.id
0
86
Given url baseUrlEvents + '/api/v1/events'
0
87
And header X-Role = 'ADMIN'
0
88
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
89
And request
0
{
"roomId": "#(roomId)",
"title": "#(eventTitle)",
"description": "Test event exhausted tier",
"date": "#(futureDate)",
"capacity": 5,
"enableSeats": false
}
100
When method post
16
22:50:36.831 request:
2 > POST http://localhost:8081/api/v1/events
2 > X-Role: ADMIN
2 > X-User-Id: 00000000-0000-0000-0000-000000000001
2 > Content-Type: application/json; charset=UTF-8
2 > Content-Length: 209
2 > Host: localhost:8081
2 > Connection: Keep-Alive
2 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
2 > Accept-Encoding: gzip,deflate
{"roomId":"10e2c018-89bf-4a37-b8e7-2bc016a4a856","title":"Avail S2 7c87cb95-f667-44df-bd2b-2331f4f4f167","description":"Test event exhausted tier","date":"2026-12-15T20:00:00","capacity":5,"enableSeats":false}
22:50:36.845 response time in milliseconds: 14
2 < 201
2 < Content-Type: application/json
2 < Transfer-Encoding: chunked
2 < Date: Wed, 08 Apr 2026 03:50:36 GMT
2 < Keep-Alive: timeout=60
2 < Connection: keep-alive
{"id":"fe5916cf-204e-48d9-8aa9-00269ee2ad53","roomId":"10e2c018-89bf-4a37-b8e7-2bc016a4a856","title":"Avail S2 7c87cb95-f667-44df-bd2b-2331f4f4f167","description":"Test event exhausted tier","date":"2026-12-15T20:00:00","capacity":5,"status":"DRAFT","createdAt":"2026-04-08T03:50:36.838884653","updatedAt":"2026-04-08T03:50:36.838897855","createdBy":"00000000-0000-0000-0000-000000000001","imageUrl":null,"subtitle":null,"location":null,"director":null,"castMembers":null,"duration":null,"tag":null,"isLimited":false,"isFeatured":false,"enableSeats":false,"author":null}
101
Then status 201
0
102
* def eventId = response.id
0
104
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/tiers'
0
105
And header X-Role = 'ADMIN'
0
106
And request
0
[{ "tierType": "GENERAL", "price": 40, "quota": 2 }]
110
When method post
16
22:50:36.848 request:
3 > POST http://localhost:8081/api/v1/events/fe5916cf-204e-48d9-8aa9-00269ee2ad53/tiers
3 > X-Role: ADMIN
3 > Content-Type: application/json; charset=UTF-8
3 > Content-Length: 45
3 > Host: localhost:8081
3 > Connection: Keep-Alive
3 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
3 > Accept-Encoding: gzip,deflate
[{"tierType":"GENERAL","price":40,"quota":2}]
22:50:36.863 response time in milliseconds: 15
3 < 201
3 < Content-Type: application/json
3 < Transfer-Encoding: chunked
3 < Date: Wed, 08 Apr 2026 03:50:36 GMT
3 < Keep-Alive: timeout=60
3 < Connection: keep-alive
{"eventId":"fe5916cf-204e-48d9-8aa9-00269ee2ad53","tiers":[{"id":"2274ff34-294a-4576-9ade-074e47f3c178","tierType":"GENERAL","price":40,"quota":2,"validFrom":null,"validUntil":null,"createdAt":"2026-04-08T03:50:36.85578232","updatedAt":"2026-04-08T03:50:36.85579937"}]}
111
Then status 201
0
112
* def tierId = response.tiers[0].id
0
114
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/publish'
0
115
And header X-Role = 'ADMIN'
0
116
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
117
When method patch
15
22:50:36.865 request:
4 > PATCH http://localhost:8081/api/v1/events/fe5916cf-204e-48d9-8aa9-00269ee2ad53/publish
4 > X-Role: ADMIN
4 > X-User-Id: 00000000-0000-0000-0000-000000000001
4 > Host: localhost:8081
4 > Connection: Keep-Alive
4 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
4 > Accept-Encoding: gzip,deflate
22:50:36.878 response time in milliseconds: 12
4 < 200
4 < Content-Type: application/json
4 < Transfer-Encoding: chunked
4 < Date: Wed, 08 Apr 2026 03:50:36 GMT
4 < Keep-Alive: timeout=60
4 < Connection: keep-alive
{"id":"fe5916cf-204e-48d9-8aa9-00269ee2ad53","roomId":"10e2c018-89bf-4a37-b8e7-2bc016a4a856","title":"Avail S2 7c87cb95-f667-44df-bd2b-2331f4f4f167","description":"Test event exhausted tier","date":"2026-12-15T20:00:00","capacity":5,"status":"PUBLISHED","createdAt":"2026-04-08T03:50:36.838885","updatedAt":"2026-04-08T03:50:36.870555216","createdBy":"00000000-0000-0000-0000-000000000001","imageUrl":null,"subtitle":null,"location":null,"director":null,"castMembers":null,"duration":null,"tag":null,"isLimited":false,"isFeatured":false,"enableSeats":false,"author":null}
118
Then status 200
0
# Reservation 1 + approved payment
121
* def email1 = 'b1s2-' + tag + '@karate.com'
1
122
Given url baseUrlTicketing + '/api/v1/reservations'
0
123
And header X-User-Id = buyer1Id
0
124
And request
1
{ "eventId": "#(eventId)", "tierId": "#(tierId)", "buyerEmail": "#(email1)" }
128
When method post
600
22:50:36.882 request:
5 > POST http://localhost:8082/api/v1/reservations
5 > X-User-Id: e05b8d9b-036b-42a1-b16a-17b8eb2bb694
5 > Content-Type: application/json; charset=UTF-8
5 > Content-Length: 166
5 > Host: localhost:8082
5 > Connection: Keep-Alive
5 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
5 > Accept-Encoding: gzip,deflate
{"eventId":"fe5916cf-204e-48d9-8aa9-00269ee2ad53","tierId":"2274ff34-294a-4576-9ade-074e47f3c178","buyerEmail":"b1s2-7c87cb95-f667-44df-bd2b-2331f4f4f167@karate.com"}
22:50:37.481 response time in milliseconds: 599
5 < 201
5 < Content-Type: application/json
5 < Transfer-Encoding: chunked
5 < Date: Wed, 08 Apr 2026 03:50:37 GMT
5 < Keep-Alive: timeout=60
5 < Connection: keep-alive
{"id":"2ebe8b5f-1db9-4f11-9395-0e6693b986ae","eventId":"fe5916cf-204e-48d9-8aa9-00269ee2ad53","tierId":"2274ff34-294a-4576-9ade-074e47f3c178","buyerId":"e05b8d9b-036b-42a1-b16a-17b8eb2bb694","status":"PENDING","createdAt":"2026-04-08T03:50:37.40949172","updatedAt":"2026-04-08T03:50:37.40949172","validUntilAt":"2026-04-08T04:00:37.40949172"}
129
Then status 201
0
130
* def res1 = response.id
0
132
Given url baseUrlTicketing + '/api/v1/reservations/' + res1 + '/payments'
1
133
And header X-User-Id = buyer1Id
0
134
And request { amount: 40, paymentMethod: 'MOCK', status: 'APPROVED' }
0
135
When method post
96
22:50:37.484 request:
6 > POST http://localhost:8082/api/v1/reservations/2ebe8b5f-1db9-4f11-9395-0e6693b986ae/payments
6 > X-User-Id: e05b8d9b-036b-42a1-b16a-17b8eb2bb694
6 > Content-Type: application/json; charset=UTF-8
6 > Content-Length: 56
6 > Host: localhost:8082
6 > Connection: Keep-Alive
6 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
6 > Accept-Encoding: gzip,deflate
{"amount":40,"paymentMethod":"MOCK","status":"APPROVED"}
22:50:37.577 response time in milliseconds: 93
6 < 200
6 < Content-Type: application/json
6 < Transfer-Encoding: chunked
6 < Date: Wed, 08 Apr 2026 03:50:37 GMT
6 < Keep-Alive: timeout=60
6 < Connection: keep-alive
{"reservationId":"2ebe8b5f-1db9-4f11-9395-0e6693b986ae","status":"CONFIRMED","ticketId":"f6a89f92-5fa5-40d7-98e8-a92e9ffaa97b","message":"Payment approved. Ticket generated.","ticket":{"ticketId":"f6a89f92-5fa5-40d7-98e8-a92e9ffaa97b","eventId":"fe5916cf-204e-48d9-8aa9-00269ee2ad53","eventTitle":"Avail S2 7c87cb95-f667-44df-bd2b-2331f4f4f167","eventDate":"2026-12-15T20:00:00","tier":"GENERAL","pricePaid":40,"status":"VALID","purchasedAt":"2026-04-08T03:50:37.522693432","buyerEmail":"b1s2-7c87cb95-f667-44df-bd2b-2331f4f4f167@karate.com","reservationId":"2ebe8b5f-1db9-4f11-9395-0e6693b986ae"},"timestamp":"2026-04-08T03:50:37.539608404"}
136
Then status 200
0
# Reservation 2 + approved payment
139
* def email2 = 'b2s2-' + tag + '@karate.com'
1
140
Given url baseUrlTicketing + '/api/v1/reservations'
0
141
And header X-User-Id = buyer2Id
0
142
And request
1
{ "eventId": "#(eventId)", "tierId": "#(tierId)", "buyerEmail": "#(email2)" }
146
When method post
38
22:50:37.581 request:
7 > POST http://localhost:8082/api/v1/reservations
7 > X-User-Id: 65b0b890-1174-4552-b842-ee5854ca89dc
7 > Content-Type: application/json; charset=UTF-8
7 > Content-Length: 166
7 > Host: localhost:8082
7 > Connection: Keep-Alive
7 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
7 > Accept-Encoding: gzip,deflate
{"eventId":"fe5916cf-204e-48d9-8aa9-00269ee2ad53","tierId":"2274ff34-294a-4576-9ade-074e47f3c178","buyerEmail":"b2s2-7c87cb95-f667-44df-bd2b-2331f4f4f167@karate.com"}
22:50:37.617 response time in milliseconds: 36
7 < 201
7 < Content-Type: application/json
7 < Transfer-Encoding: chunked
7 < Date: Wed, 08 Apr 2026 03:50:37 GMT
7 < Keep-Alive: timeout=60
7 < Connection: keep-alive
{"id":"14ed702d-0e67-49b7-8ffd-96dfe9851ada","eventId":"fe5916cf-204e-48d9-8aa9-00269ee2ad53","tierId":"2274ff34-294a-4576-9ade-074e47f3c178","buyerId":"65b0b890-1174-4552-b842-ee5854ca89dc","status":"PENDING","createdAt":"2026-04-08T03:50:37.609754075","updatedAt":"2026-04-08T03:50:37.609754075","validUntilAt":"2026-04-08T04:00:37.609754075"}
147
Then status 201
0
148
* def res2 = response.id
0
151
Given url baseUrlTicketing + '/api/v1/reservations/' + res2 + '/payments'
1
152
And header X-User-Id = buyer2Id
0
153
And request { amount: 40, paymentMethod: 'MOCK', status: 'APPROVED' }
0
154
When method post
36
22:50:37.621 request:
8 > POST http://localhost:8082/api/v1/reservations/14ed702d-0e67-49b7-8ffd-96dfe9851ada/payments
8 > X-User-Id: 65b0b890-1174-4552-b842-ee5854ca89dc
8 > Content-Type: application/json; charset=UTF-8
8 > Content-Length: 56
8 > Host: localhost:8082
8 > Connection: Keep-Alive
8 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
8 > Accept-Encoding: gzip,deflate
{"amount":40,"paymentMethod":"MOCK","status":"APPROVED"}
22:50:37.655 response time in milliseconds: 33
8 < 200
8 < Content-Type: application/json
8 < Transfer-Encoding: chunked
8 < Date: Wed, 08 Apr 2026 03:50:37 GMT
8 < Keep-Alive: timeout=60
8 < Connection: keep-alive
{"reservationId":"14ed702d-0e67-49b7-8ffd-96dfe9851ada","status":"CONFIRMED","ticketId":"3c66f05b-05ff-4e49-9623-46197f90e395","message":"Payment approved. Ticket generated.","ticket":{"ticketId":"3c66f05b-05ff-4e49-9623-46197f90e395","eventId":"fe5916cf-204e-48d9-8aa9-00269ee2ad53","eventTitle":"Avail S2 7c87cb95-f667-44df-bd2b-2331f4f4f167","eventDate":"2026-12-15T20:00:00","tier":"GENERAL","pricePaid":40,"status":"VALID","purchasedAt":"2026-04-08T03:50:37.636089702","buyerEmail":"b2s2-7c87cb95-f667-44df-bd2b-2331f4f4f167@karate.com","reservationId":"14ed702d-0e67-49b7-8ffd-96dfe9851ada"},"timestamp":"2026-04-08T03:50:37.644781256"}
155
Then status 200
0
# HU-03: Verify tier shows as unavailable (isAvailable = false)
# Note: 'quota' in availableTiers is the REMAINING quota (decremented per approved payment)
# After 2 approved payments on quota=2, remaining quota = 0 and isAvailable = false
160
Given url baseUrlEvents + '/api/v1/events/' + eventId
0
161
When method get
9
22:50:37.656 request:
9 > GET http://localhost:8081/api/v1/events/fe5916cf-204e-48d9-8aa9-00269ee2ad53
9 > Host: localhost:8081
9 > Connection: Keep-Alive
9 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
9 > Accept-Encoding: gzip,deflate
22:50:37.664 response time in milliseconds: 8
9 < 200
9 < Content-Type: application/json
9 < Transfer-Encoding: chunked
9 < Date: Wed, 08 Apr 2026 03:50:37 GMT
9 < Keep-Alive: timeout=60
9 < Connection: keep-alive
{"id":"fe5916cf-204e-48d9-8aa9-00269ee2ad53","title":"Avail S2 7c87cb95-f667-44df-bd2b-2331f4f4f167","description":"Test event exhausted tier","date":"2026-12-15T20:00:00","capacity":5,"room":{"id":"10e2c018-89bf-4a37-b8e7-2bc016a4a856","name":"Exhausted Tier Room S2","maxCapacity":50,"created_at":"2026-04-08T03:50:36.820136","updated_at":"2026-04-08T03:50:36.820148"},"availableTiers":[{"id":"2274ff34-294a-4576-9ade-074e47f3c178","tierType":"GENERAL","price":40.00,"quota":0,"validFrom":null,"validUntil":null,"isAvailable":false,"reason":"SOLD_OUT"}],"created_at":"2026-04-08T03:50:36.838885","imageUrl":null,"subtitle":null,"location":null,"director":null,"castMembers":null,"duration":null,"tag":null,"isLimited":false,"isFeatured":false,"enableSeats":false,"author":null}
162
Then status 200
0
163
* def exhaustedTier = response.availableTiers[0]
0
164
* print 'Exhausted tier:', exhaustedTier
1
22:50:37.666 [print] Exhausted tier: {
"id": "2274ff34-294a-4576-9ade-074e47f3c178",
"tierType": "GENERAL",
"price": 40.0,
"quota": 0,
"validFrom": null,
"validUntil": null,
"isAvailable": false,
"reason": "SOLD_OUT"
}
165
* match exhaustedTier.quota == 0
0
166
* match exhaustedTier.isAvailable == false
0
167
* print 'Scenario 2 PASS: Tier unavailable after 2 approvals (quota=0, isAvailable=false)'
0
22:50:37.667 [print] Scenario 2 PASS: Tier unavailable after 2 approvals (quota=0, isAvailable=false)
Scenario: [3:170]
Scenario 3 - Early Bird tier not yet started (validFrom in future -> isAvailable false)
ms: 72
>>
Background:
7
* def baseUrlEvents = karate.properties['baseUrlEvents'] || 'http://localhost:8081'
0
22:50:37.670 karate.env system property was: null
8
* def baseUrlTicketing = karate.properties['baseUrlTicketing'] || 'http://localhost:8082'
0
9
* def UUID = Java.type('java.util.UUID')
0
10
* def futureDate = '2026-12-15T20:00:00'
0
# Note: Backend validates validFrom/validUntil must be in the FUTURE at creation time.
# A tier with validFrom in the future (not yet started) shows isAvailable=false.
# This scenario verifies the EB tier availability logic without requiring past-dated creation.
176
* def tag = UUID.randomUUID() + ''
0
177
* def eventTitle = 'Avail S3 ' + tag
1
179
Given url baseUrlEvents + '/api/v1/rooms'
0
180
And header X-Role = 'ADMIN'
0
181
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
182
And request { name: 'Early Bird Not-Started Room S3', maxCapacity: 100 }
0
183
When method post
11
22:50:37.674 request:
1 > POST http://localhost:8081/api/v1/rooms
1 > X-Role: ADMIN
1 > X-User-Id: 00000000-0000-0000-0000-000000000001
1 > Content-Type: application/json; charset=UTF-8
1 > Content-Length: 59
1 > Host: localhost:8081
1 > Connection: Keep-Alive
1 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
1 > Accept-Encoding: gzip,deflate
{"name":"Early Bird Not-Started Room S3","maxCapacity":100}
22:50:37.685 response time in milliseconds: 10
1 < 201
1 < Content-Type: application/json
1 < Transfer-Encoding: chunked
1 < Date: Wed, 08 Apr 2026 03:50:37 GMT
1 < Keep-Alive: timeout=60
1 < Connection: keep-alive
{"id":"83c3543e-106d-4563-97da-4a8b0a4090f9","name":"Early Bird Not-Started Room S3","maxCapacity":100,"created_at":"2026-04-08T03:50:37.677315027","updated_at":"2026-04-08T03:50:37.677331715"}
184
Then status 201
0
185
* def roomId = response.id
0
187
Given url baseUrlEvents + '/api/v1/events'
0
188
And header X-Role = 'ADMIN'
0
189
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
190
And request
0
{
"roomId": "#(roomId)",
"title": "#(eventTitle)",
"description": "Test event - EB not yet started",
"date": "#(futureDate)",
"capacity": 100,
"enableSeats": false
}
201
When method post
18
22:50:37.686 request:
2 > POST http://localhost:8081/api/v1/events
2 > X-Role: ADMIN
2 > X-User-Id: 00000000-0000-0000-0000-000000000001
2 > Content-Type: application/json; charset=UTF-8
2 > Content-Length: 217
2 > Host: localhost:8081
2 > Connection: Keep-Alive
2 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
2 > Accept-Encoding: gzip,deflate
{"roomId":"83c3543e-106d-4563-97da-4a8b0a4090f9","title":"Avail S3 7dde01e9-8454-4e82-b035-10ae3f3fcd61","description":"Test event - EB not yet started","date":"2026-12-15T20:00:00","capacity":100,"enableSeats":false}
22:50:37.703 response time in milliseconds: 17
2 < 201
2 < Content-Type: application/json
2 < Transfer-Encoding: chunked
2 < Date: Wed, 08 Apr 2026 03:50:37 GMT
2 < Keep-Alive: timeout=60
2 < Connection: keep-alive
{"id":"28f55f3a-fdaf-43e9-b26e-02e4ed2ce8b9","roomId":"83c3543e-106d-4563-97da-4a8b0a4090f9","title":"Avail S3 7dde01e9-8454-4e82-b035-10ae3f3fcd61","description":"Test event - EB not yet started","date":"2026-12-15T20:00:00","capacity":100,"status":"DRAFT","createdAt":"2026-04-08T03:50:37.69486117","updatedAt":"2026-04-08T03:50:37.694877077","createdBy":"00000000-0000-0000-0000-000000000001","imageUrl":null,"subtitle":null,"location":null,"director":null,"castMembers":null,"duration":null,"tag":null,"isLimited":false,"isFeatured":false,"enableSeats":false,"author":null}
202
Then status 201
0
203
* def eventId = response.id
0
# Create GENERAL tier first so event can be published (EB tier alone with future validFrom
# causes 422 on publish since no active tiers exist at publish time)
207
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/tiers'
0
208
And header X-Role = 'ADMIN'
0
209
And request
0
[{ "tierType": "GENERAL", "price": 50, "quota": 80 }]
213
When method post
13
22:50:37.705 request:
3 > POST http://localhost:8081/api/v1/events/28f55f3a-fdaf-43e9-b26e-02e4ed2ce8b9/tiers
3 > X-Role: ADMIN
3 > Content-Type: application/json; charset=UTF-8
3 > Content-Length: 46
3 > Host: localhost:8081
3 > Connection: Keep-Alive
3 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
3 > Accept-Encoding: gzip,deflate
[{"tierType":"GENERAL","price":50,"quota":80}]
22:50:37.717 response time in milliseconds: 12
3 < 201
3 < Content-Type: application/json
3 < Transfer-Encoding: chunked
3 < Date: Wed, 08 Apr 2026 03:50:37 GMT
3 < Keep-Alive: timeout=60
3 < Connection: keep-alive
{"eventId":"28f55f3a-fdaf-43e9-b26e-02e4ed2ce8b9","tiers":[{"id":"e9e10ce3-844c-4852-8263-beddfb37aca4","tierType":"GENERAL","price":50,"quota":80,"validFrom":null,"validUntil":null,"createdAt":"2026-04-08T03:50:37.710536696","updatedAt":"2026-04-08T03:50:37.710550381"}]}
214
Then status 201
0
216
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/publish'
0
217
And header X-Role = 'ADMIN'
0
218
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
219
When method patch
15
22:50:37.719 request:
4 > PATCH http://localhost:8081/api/v1/events/28f55f3a-fdaf-43e9-b26e-02e4ed2ce8b9/publish
4 > X-Role: ADMIN
4 > X-User-Id: 00000000-0000-0000-0000-000000000001
4 > Host: localhost:8081
4 > Connection: Keep-Alive
4 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
4 > Accept-Encoding: gzip,deflate
22:50:37.733 response time in milliseconds: 14
4 < 200
4 < Content-Type: application/json
4 < Transfer-Encoding: chunked
4 < Date: Wed, 08 Apr 2026 03:50:37 GMT
4 < Keep-Alive: timeout=60
4 < Connection: keep-alive
{"id":"28f55f3a-fdaf-43e9-b26e-02e4ed2ce8b9","roomId":"83c3543e-106d-4563-97da-4a8b0a4090f9","title":"Avail S3 7dde01e9-8454-4e82-b035-10ae3f3fcd61","description":"Test event - EB not yet started","date":"2026-12-15T20:00:00","capacity":100,"status":"PUBLISHED","createdAt":"2026-04-08T03:50:37.694861","updatedAt":"2026-04-08T03:50:37.725447192","createdBy":"00000000-0000-0000-0000-000000000001","imageUrl":null,"subtitle":null,"location":null,"director":null,"castMembers":null,"duration":null,"tag":null,"isLimited":false,"isFeatured":false,"enableSeats":false,"author":null}
220
Then status 200
0
# HU-03: Verify event shows GENERAL tier as available
223
Given url baseUrlEvents + '/api/v1/events/' + eventId
0
224
When method get
9
22:50:37.734 request:
5 > GET http://localhost:8081/api/v1/events/28f55f3a-fdaf-43e9-b26e-02e4ed2ce8b9
5 > Host: localhost:8081
5 > Connection: Keep-Alive
5 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
5 > Accept-Encoding: gzip,deflate
22:50:37.743 response time in milliseconds: 8
5 < 200
5 < Content-Type: application/json
5 < Transfer-Encoding: chunked
5 < Date: Wed, 08 Apr 2026 03:50:37 GMT
5 < Keep-Alive: timeout=60
5 < Connection: keep-alive
{"id":"28f55f3a-fdaf-43e9-b26e-02e4ed2ce8b9","title":"Avail S3 7dde01e9-8454-4e82-b035-10ae3f3fcd61","description":"Test event - EB not yet started","date":"2026-12-15T20:00:00","capacity":100,"room":{"id":"83c3543e-106d-4563-97da-4a8b0a4090f9","name":"Early Bird Not-Started Room S3","maxCapacity":100,"created_at":"2026-04-08T03:50:37.677315","updated_at":"2026-04-08T03:50:37.677332"},"availableTiers":[{"id":"e9e10ce3-844c-4852-8263-beddfb37aca4","tierType":"GENERAL","price":50.00,"quota":80,"validFrom":null,"validUntil":null,"isAvailable":true,"reason":null}],"created_at":"2026-04-08T03:50:37.694861","imageUrl":null,"subtitle":null,"location":null,"director":null,"castMembers":null,"duration":null,"tag":null,"isLimited":false,"isFeatured":false,"enableSeats":false,"author":null}
225
Then status 200
0
226
* def genTier = response.availableTiers[0]
0
227
* match genTier.tierType == 'GENERAL'
0
228
* match genTier.isAvailable == true
0
229
* print 'Scenario 3 PASS: GENERAL tier is available (isAvailable=true)'
0
22:50:37.744 [print] Scenario 3 PASS: GENERAL tier is available (isAvailable=true)
230
* print 'Note: EB tier with past validUntil cannot be created via API (backend validates future dates).'
0
22:50:37.744 [print] Note: EB tier with past validUntil cannot be created via API (backend validates future dates).
231
* print 'Expiration-based isAvailable=false for EB is validated in expiration-release-flow-with-sql.feature'
0
22:50:37.745 [print] Expiration-based isAvailable=false for EB is validated in expiration-release-flow-with-sql.feature
Scenario: [4:234]
Scenario 4 - Event with no active tiers (empty availableTiers)
ms: 40
>>
Background:
7
* def baseUrlEvents = karate.properties['baseUrlEvents'] || 'http://localhost:8081'
0
22:50:37.747 karate.env system property was: null
8
* def baseUrlTicketing = karate.properties['baseUrlTicketing'] || 'http://localhost:8082'
0
9
* def UUID = Java.type('java.util.UUID')
0
10
* def futureDate = '2026-12-15T20:00:00'
0
236
* def tag = UUID.randomUUID() + ''
0
237
* def eventTitle = 'Avail S4 ' + tag
0
239
Given url baseUrlEvents + '/api/v1/rooms'
0
240
And header X-Role = 'ADMIN'
0
241
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
242
And request { name: 'No Tiers Room S4', maxCapacity: 50 }
0
243
When method post
11
22:50:37.750 request:
1 > POST http://localhost:8081/api/v1/rooms
1 > X-Role: ADMIN
1 > X-User-Id: 00000000-0000-0000-0000-000000000001
1 > Content-Type: application/json; charset=UTF-8
1 > Content-Length: 44
1 > Host: localhost:8081
1 > Connection: Keep-Alive
1 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
1 > Accept-Encoding: gzip,deflate
{"name":"No Tiers Room S4","maxCapacity":50}
22:50:37.760 response time in milliseconds: 10
1 < 201
1 < Content-Type: application/json
1 < Transfer-Encoding: chunked
1 < Date: Wed, 08 Apr 2026 03:50:37 GMT
1 < Keep-Alive: timeout=60
1 < Connection: keep-alive
{"id":"f134d632-df5b-4a3f-9173-fe43d8474764","name":"No Tiers Room S4","maxCapacity":50,"created_at":"2026-04-08T03:50:37.753393997","updated_at":"2026-04-08T03:50:37.753408466"}
244
Then status 201
0
245
* def roomId = response.id
0
247
Given url baseUrlEvents + '/api/v1/events'
0
248
And header X-Role = 'ADMIN'
0
249
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
250
And request
0
{
"roomId": "#(roomId)",
"title": "#(eventTitle)",
"description": "Test event with no tiers",
"date": "#(futureDate)",
"capacity": 50,
"enableSeats": false
}
261
When method post
16
22:50:37.761 request:
2 > POST http://localhost:8081/api/v1/events
2 > X-Role: ADMIN
2 > X-User-Id: 00000000-0000-0000-0000-000000000001
2 > Content-Type: application/json; charset=UTF-8
2 > Content-Length: 209
2 > Host: localhost:8081
2 > Connection: Keep-Alive
2 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
2 > Accept-Encoding: gzip,deflate
{"roomId":"f134d632-df5b-4a3f-9173-fe43d8474764","title":"Avail S4 48ecf8af-3062-4a57-b1ba-926fa3e7f5cf","description":"Test event with no tiers","date":"2026-12-15T20:00:00","capacity":50,"enableSeats":false}
22:50:37.776 response time in milliseconds: 15
2 < 201
2 < Content-Type: application/json
2 < Transfer-Encoding: chunked
2 < Date: Wed, 08 Apr 2026 03:50:37 GMT
2 < Keep-Alive: timeout=60
2 < Connection: keep-alive
{"id":"6065c5a2-3d92-492f-b0ac-6319c0825a7e","roomId":"f134d632-df5b-4a3f-9173-fe43d8474764","title":"Avail S4 48ecf8af-3062-4a57-b1ba-926fa3e7f5cf","description":"Test event with no tiers","date":"2026-12-15T20:00:00","capacity":50,"status":"DRAFT","createdAt":"2026-04-08T03:50:37.767953796","updatedAt":"2026-04-08T03:50:37.767964186","createdBy":"00000000-0000-0000-0000-000000000001","imageUrl":null,"subtitle":null,"location":null,"director":null,"castMembers":null,"duration":null,"tag":null,"isLimited":false,"isFeatured":false,"enableSeats":false,"author":null}
262
Then status 201
0
263
* def eventId = response.id
0
# HU-03: Backend constraint: Event without tiers CANNOT be published (returns 422 EVENT_HAS_NO_TIERS)
# This validates that the availability system enforces at least 1 tier before publishing
267
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/publish'
0
268
And header X-Role = 'ADMIN'
0
269
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
270
When method patch
10
22:50:37.778 request:
3 > PATCH http://localhost:8081/api/v1/events/6065c5a2-3d92-492f-b0ac-6319c0825a7e/publish
3 > X-Role: ADMIN
3 > X-User-Id: 00000000-0000-0000-0000-000000000001
3 > Host: localhost:8081
3 > Connection: Keep-Alive
3 > User-Agent: Apache-HttpClient/4.5.14 (Java/17.0.17)
3 > Accept-Encoding: gzip,deflate
22:50:37.787 response time in milliseconds: 9
3 < 422
3 < Content-Type: application/json
3 < Transfer-Encoding: chunked
3 < Date: Wed, 08 Apr 2026 03:50:37 GMT
3 < Keep-Alive: timeout=60
3 < Connection: keep-alive
{"error":"EVENT_HAS_NO_TIERS","message":"Event with id '6065c5a2-3d92-492f-b0ac-6319c0825a7e' cannot be published: no tiers configured"}
271
Then status 422
0
272
* match response.error == 'EVENT_HAS_NO_TIERS'
1
273
* print 'Scenario 4 PASS: Event with no tiers cannot be published (422 EVENT_HAS_NO_TIERS - business constraint enforced)'
1
22:50:37.789 [print] Scenario 4 PASS: Event with no tiers cannot be published (422 EVENT_HAS_NO_TIERS - business constraint enforced)