Summary| Tags| Feature: api/ticket-visibility-and-access-control/ticket-visibility-and-access-control.feature| Ticketing MVP - Ticket Visibility and Access Control As a Ticketing MVP automation I want to validate ticket visibility, consistency, and access control So that I can verify tickets are secure and data is correct
Scenario: [1:20] Scenario 1 - Ticket Visible for Owner (Happy Path)
ms: 113
>>
Background:
7
* def baseUrlEvents = karate.properties['baseUrlEvents'] || 'http://localhost:8081'
0
22:54:52.385 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 buyerId1 = UUID.randomUUID() + ''
0
11
* def buyerId2 = UUID.randomUUID() + ''
0
12
* def buyerId3 = UUID.randomUUID() + ''
0
13
* def buyerEmail1 = 'buyer1-' + UUID.randomUUID() + '@karate-test.com'
0
14
* def buyerEmail2 = 'buyer2-' + UUID.randomUUID() + '@karate-test.com'
0
15
* def buyerEmail3 = 'buyer3-' + UUID.randomUUID() + '@karate-test.com'
0
16
* def eventTitle = 'Karate Ticket Test Event ' + UUID.randomUUID()
0
17
* def futureDate = '2026-12-15T20:00:00'
0
# Setup: Create Room
23
Given url baseUrlEvents + '/api/v1/rooms'
0
24
And header X-Role = 'ADMIN'
0
25
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
26
And request
0
{ "name": "Karate Test Room - Ticket Visibility", "maxCapacity": 100 }
33
When method post
11
22:54:52.388 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: 65 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":"Karate Test Room - Ticket Visibility","maxCapacity":100} 22:54:52.399 response time in milliseconds: 10 1 < 201 1 < Content-Type: application/json 1 < Transfer-Encoding: chunked 1 < Date: Wed, 08 Apr 2026 03:54:51 GMT 1 < Keep-Alive: timeout=60 1 < Connection: keep-alive {"id":"23957959-a832-42d6-aa86-a82084d694a7","name":"Karate Test Room - Ticket Visibility","maxCapacity":100,"created_at":"2026-04-08T03:54:52.390771034","updated_at":"2026-04-08T03:54:52.390777697"}
34
Then status 201
0
35
* def roomId = response.id ? response.id : response.roomId
0
36
* print 'Room created:', roomId
0
22:54:52.399 [print] Room created: 23957959-a832-42d6-aa86-a82084d694a7
# Setup: Create Draft Event
39
Given url baseUrlEvents + '/api/v1/events'
0
40
And header X-Role = 'ADMIN'
0
41
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
42
And request
0
{ "roomId": "#(roomId)", "title": "#(eventTitle)", "description": "Test event for ticket visibility", "date": "#(futureDate)", "capacity": 50, "enableSeats": false }
53
When method post
12
22:54:52.400 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: 233 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":"23957959-a832-42d6-aa86-a82084d694a7","title":"Karate Ticket Test Event af9a9f73-26de-453d-86a6-6a15a2483364","description":"Test event for ticket visibility","date":"2026-12-15T20:00:00","capacity":50,"enableSeats":false} 22:54:52.412 response time in milliseconds: 11 2 < 201 2 < Content-Type: application/json 2 < Transfer-Encoding: chunked 2 < Date: Wed, 08 Apr 2026 03:54:51 GMT 2 < Keep-Alive: timeout=60 2 < Connection: keep-alive {"id":"b5bda42b-d599-4bf6-8d25-88b4fc041a14","roomId":"23957959-a832-42d6-aa86-a82084d694a7","title":"Karate Ticket Test Event af9a9f73-26de-453d-86a6-6a15a2483364","description":"Test event for ticket visibility","date":"2026-12-15T20:00:00","capacity":50,"status":"DRAFT","createdAt":"2026-04-08T03:54:52.405048845","updatedAt":"2026-04-08T03:54:52.405053699","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}
54
Then status 201
0
55
* def eventId = response.id ? response.id : response.eventId
0
56
* print 'Event created in DRAFT:', eventId
0
22:54:52.412 [print] Event created in DRAFT: b5bda42b-d599-4bf6-8d25-88b4fc041a14
# Setup: Configure Tier
59
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/tiers'
0
60
And header X-Role = 'ADMIN'
0
61
And request
0
[ { "tierType": "GENERAL", "price": 100, "quota": 40 } ]
71
When method post
11
22:54:52.413 request: 3 > POST http://localhost:8081/api/v1/events/b5bda42b-d599-4bf6-8d25-88b4fc041a14/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":100,"quota":40}] 22:54:52.423 response time in milliseconds: 10 3 < 201 3 < Content-Type: application/json 3 < Transfer-Encoding: chunked 3 < Date: Wed, 08 Apr 2026 03:54:51 GMT 3 < Keep-Alive: timeout=60 3 < Connection: keep-alive {"eventId":"b5bda42b-d599-4bf6-8d25-88b4fc041a14","tiers":[{"id":"a08973d5-d88b-4cfa-8c8a-ed2c9f9eeb60","tierType":"GENERAL","price":100,"quota":40,"validFrom":null,"validUntil":null,"createdAt":"2026-04-08T03:54:52.417543967","updatedAt":"2026-04-08T03:54:52.417552659"}]}
72
Then status 201
0
73
* def tierBlock = response.tiers ? response.tiers[0] : response[0]
0
74
* def tierId = tierBlock.id ? tierBlock.id : tierBlock.tierId
0
75
* print 'Tier configured:', tierId
0
22:54:52.424 [print] Tier configured: a08973d5-d88b-4cfa-8c8a-ed2c9f9eeb60
# Setup: Publish Event
78
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/publish'
0
79
And header X-Role = 'ADMIN'
0
80
When method patch
10
22:54:52.425 request: 4 > PATCH http://localhost:8081/api/v1/events/b5bda42b-d599-4bf6-8d25-88b4fc041a14/publish 4 > X-Role: ADMIN 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:54:52.433 response time in milliseconds: 8 4 < 200 4 < Content-Type: application/json 4 < Transfer-Encoding: chunked 4 < Date: Wed, 08 Apr 2026 03:54:51 GMT 4 < Keep-Alive: timeout=60 4 < Connection: keep-alive {"id":"b5bda42b-d599-4bf6-8d25-88b4fc041a14","roomId":"23957959-a832-42d6-aa86-a82084d694a7","title":"Karate Ticket Test Event af9a9f73-26de-453d-86a6-6a15a2483364","description":"Test event for ticket visibility","date":"2026-12-15T20:00:00","capacity":50,"status":"PUBLISHED","createdAt":"2026-04-08T03:54:52.405049","updatedAt":"2026-04-08T03:54:52.427823164","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}
81
Then status 200
0
82
* print 'Event published'
0
22:54:52.434 [print] Event published
# HU-07 Path: Create Reservation (Buyer 1)
85
Given url baseUrlTicketing + '/api/v1/reservations'
0
86
And header X-User-Id = buyerId1
0
87
And request
0
{ "eventId": "#(eventId)", "tierId": "#(tierId)", "buyerEmail": "#(buyerEmail1)" }
95
When method post
26
22:54:52.435 request: 5 > POST http://localhost:8082/api/v1/reservations 5 > X-User-Id: 55c7b992-9011-48a5-87c0-1ea4bd9068b9 5 > Content-Type: application/json; charset=UTF-8 5 > Content-Length: 173 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":"b5bda42b-d599-4bf6-8d25-88b4fc041a14","tierId":"a08973d5-d88b-4cfa-8c8a-ed2c9f9eeb60","buyerEmail":"buyer1-93b06b54-7f62-4aa7-a8ea-3fa57fa68bab@karate-test.com"} 22:54:52.460 response time in milliseconds: 25 5 < 201 5 < Content-Type: application/json 5 < Transfer-Encoding: chunked 5 < Date: Wed, 08 Apr 2026 03:54:51 GMT 5 < Keep-Alive: timeout=60 5 < Connection: keep-alive {"id":"7cb2663b-a4eb-470a-8306-01e292773a6c","eventId":"b5bda42b-d599-4bf6-8d25-88b4fc041a14","tierId":"a08973d5-d88b-4cfa-8c8a-ed2c9f9eeb60","buyerId":"55c7b992-9011-48a5-87c0-1ea4bd9068b9","status":"PENDING","createdAt":"2026-04-08T03:54:52.453848558","updatedAt":"2026-04-08T03:54:52.453848558","validUntilAt":"2026-04-08T04:04:52.453848558"}
96
Then status 201
0
97
* def reservationId = response.id
0
98
* print 'Reservation created:', reservationId
0
22:54:52.461 [print] Reservation created: 7cb2663b-a4eb-470a-8306-01e292773a6c
# HU-07 Path: Approved Payment (triggers ticket generation)
101
Given url baseUrlTicketing + '/api/v1/reservations/' + reservationId + '/payments'
0
102
And header X-User-Id = buyerId1
0
103
And request
0
{ "amount": 100, "paymentMethod": "MOCK", "status": "APPROVED" }
111
When method post
23
22:54:52.462 request: 6 > POST http://localhost:8082/api/v1/reservations/7cb2663b-a4eb-470a-8306-01e292773a6c/payments 6 > X-User-Id: 55c7b992-9011-48a5-87c0-1ea4bd9068b9 6 > Content-Type: application/json; charset=UTF-8 6 > Content-Length: 57 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":100,"paymentMethod":"MOCK","status":"APPROVED"} 22:54:52.484 response time in milliseconds: 22 6 < 200 6 < Content-Type: application/json 6 < Transfer-Encoding: chunked 6 < Date: Wed, 08 Apr 2026 03:54:51 GMT 6 < Keep-Alive: timeout=60 6 < Connection: keep-alive {"reservationId":"7cb2663b-a4eb-470a-8306-01e292773a6c","status":"CONFIRMED","ticketId":"5c8980ca-5193-4dfd-bd3a-27fad5f9717b","message":"Payment approved. Ticket generated.","ticket":{"ticketId":"5c8980ca-5193-4dfd-bd3a-27fad5f9717b","eventId":"b5bda42b-d599-4bf6-8d25-88b4fc041a14","eventTitle":"Karate Ticket Test Event af9a9f73-26de-453d-86a6-6a15a2483364","eventDate":"2026-12-15T20:00:00","tier":"GENERAL","pricePaid":100,"status":"VALID","purchasedAt":"2026-04-08T03:54:52.471200712","buyerEmail":"buyer1-93b06b54-7f62-4aa7-a8ea-3fa57fa68bab@karate-test.com","reservationId":"7cb2663b-a4eb-470a-8306-01e292773a6c"},"timestamp":"2026-04-08T03:54:52.477341192"}
112
Then status 200
0
113
* match response.reservationId == reservationId
0
114
* match response.status == 'CONFIRMED'
0
115
* def ticketId = response.ticketId
0
116
* print 'Payment approved, ticket generated:', ticketId
0
22:54:52.485 [print] Payment approved, ticket generated: 5c8980ca-5193-4dfd-bd3a-27fad5f9717b
# HU-07: GET /api/v1/tickets/{ticketId} as owner (Buyer 1)
119
Given url baseUrlTicketing + '/api/v1/tickets/' + ticketId
0
120
And header X-User-Id = buyerId1
0
121
When method get
14
22:54:52.485 request: 7 > GET http://localhost:8082/api/v1/tickets/5c8980ca-5193-4dfd-bd3a-27fad5f9717b 7 > X-User-Id: 55c7b992-9011-48a5-87c0-1ea4bd9068b9 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 22:54:52.499 response time in milliseconds: 14 7 < 200 7 < Content-Type: application/json 7 < Transfer-Encoding: chunked 7 < Date: Wed, 08 Apr 2026 03:54:51 GMT 7 < Keep-Alive: timeout=60 7 < Connection: keep-alive {"ticketId":"5c8980ca-5193-4dfd-bd3a-27fad5f9717b","eventId":"b5bda42b-d599-4bf6-8d25-88b4fc041a14","eventTitle":"Karate Ticket Test Event af9a9f73-26de-453d-86a6-6a15a2483364","eventDate":"2026-12-15T20:00:00","tier":"GENERAL","pricePaid":100.00,"status":"VALID","purchasedAt":"2026-04-08T03:54:52.471201","buyerEmail":"buyer1-93b06b54-7f62-4aa7-a8ea-3fa57fa68bab@karate-test.com","reservationId":"7cb2663b-a4eb-470a-8306-01e292773a6c"}
122
Then status 200
0
123
* match response == { ticketId: '#uuid', eventId: '#uuid', eventTitle: '#string', eventDate: '#string', tier: '#string', pricePaid: '#number', status: '#string', buyerEmail: '#string', reservationId: '#uuid', purchasedAt: '#string' }
0
124
* match response.ticketId == ticketId
0
125
* match response.eventId == eventId
0
126
* match response.eventTitle == eventTitle
0
127
* match response.tier == 'GENERAL'
0
128
* match response.pricePaid == 100
0
129
* match response.buyerEmail == buyerEmail1
0
130
* match response.reservationId == reservationId
0
131
* print 'Ticket retrieved successfully for owner:', ticketId
0
22:54:52.500 [print] Ticket retrieved successfully for owner: 5c8980ca-5193-4dfd-bd3a-27fad5f9717b
Scenario: [2:134] Scenario 2 - No Ticket on Declined Payment (Negative Path)
ms: 90
>>
Background:
7
* def baseUrlEvents = karate.properties['baseUrlEvents'] || 'http://localhost:8081'
0
22:54:52.501 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 buyerId1 = UUID.randomUUID() + ''
0
11
* def buyerId2 = UUID.randomUUID() + ''
0
12
* def buyerId3 = UUID.randomUUID() + ''
0
13
* def buyerEmail1 = 'buyer1-' + UUID.randomUUID() + '@karate-test.com'
0
14
* def buyerEmail2 = 'buyer2-' + UUID.randomUUID() + '@karate-test.com'
0
15
* def buyerEmail3 = 'buyer3-' + UUID.randomUUID() + '@karate-test.com'
0
16
* def eventTitle = 'Karate Ticket Test Event ' + UUID.randomUUID()
0
17
* def futureDate = '2026-12-15T20:00:00'
0
# Setup: Create Room
137
Given url baseUrlEvents + '/api/v1/rooms'
0
138
And header X-Role = 'ADMIN'
0
139
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
140
And request
0
{ "name": "Karate Test Room - Declined Payment", "maxCapacity": 100 }
147
When method post
9
22:54:52.503 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: 64 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":"Karate Test Room - Declined Payment","maxCapacity":100} 22:54:52.511 response time in milliseconds: 8 1 < 201 1 < Content-Type: application/json 1 < Transfer-Encoding: chunked 1 < Date: Wed, 08 Apr 2026 03:54:51 GMT 1 < Keep-Alive: timeout=60 1 < Connection: keep-alive {"id":"ec7fe579-55a7-4125-adc3-06b0259c72fc","name":"Karate Test Room - Declined Payment","maxCapacity":100,"created_at":"2026-04-08T03:54:52.505467837","updated_at":"2026-04-08T03:54:52.505476651"}
148
Then status 201
0
149
* def roomId = response.id ? response.id : response.roomId
0
150
* print 'Room created:', roomId
0
22:54:52.512 [print] Room created: ec7fe579-55a7-4125-adc3-06b0259c72fc
# Setup: Create Draft Event
153
Given url baseUrlEvents + '/api/v1/events'
0
154
And header X-Role = 'ADMIN'
0
155
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
156
And request
0
{ "roomId": "#(roomId)", "title": "#(eventTitle)", "description": "Test event for declined payment", "date": "#(futureDate)", "capacity": 50, "enableSeats": false }
167
When method post
12
22:54:52.512 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: 232 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":"ec7fe579-55a7-4125-adc3-06b0259c72fc","title":"Karate Ticket Test Event 6cc7d637-3736-4d2c-a67d-27c72ed67a52","description":"Test event for declined payment","date":"2026-12-15T20:00:00","capacity":50,"enableSeats":false} 22:54:52.524 response time in milliseconds: 12 2 < 201 2 < Content-Type: application/json 2 < Transfer-Encoding: chunked 2 < Date: Wed, 08 Apr 2026 03:54:51 GMT 2 < Keep-Alive: timeout=60 2 < Connection: keep-alive {"id":"c0b9a716-a59a-43fd-a531-d9ed19523334","roomId":"ec7fe579-55a7-4125-adc3-06b0259c72fc","title":"Karate Ticket Test Event 6cc7d637-3736-4d2c-a67d-27c72ed67a52","description":"Test event for declined payment","date":"2026-12-15T20:00:00","capacity":50,"status":"DRAFT","createdAt":"2026-04-08T03:54:52.517869029","updatedAt":"2026-04-08T03:54:52.517876124","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}
168
Then status 201
0
169
* def eventId = response.id ? response.id : response.eventId
0
170
* print 'Event created in DRAFT:', eventId
0
22:54:52.524 [print] Event created in DRAFT: c0b9a716-a59a-43fd-a531-d9ed19523334
# Setup: Configure Tier
173
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/tiers'
0
174
And header X-Role = 'ADMIN'
0
175
And request
0
[ { "tierType": "GENERAL", "price": 100, "quota": 40 } ]
185
When method post
10
22:54:52.525 request: 3 > POST http://localhost:8081/api/v1/events/c0b9a716-a59a-43fd-a531-d9ed19523334/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":100,"quota":40}] 22:54:52.534 response time in milliseconds: 9 3 < 201 3 < Content-Type: application/json 3 < Transfer-Encoding: chunked 3 < Date: Wed, 08 Apr 2026 03:54:51 GMT 3 < Keep-Alive: timeout=60 3 < Connection: keep-alive {"eventId":"c0b9a716-a59a-43fd-a531-d9ed19523334","tiers":[{"id":"f12125db-92fa-4de5-9aba-49c259687ea5","tierType":"GENERAL","price":100,"quota":40,"validFrom":null,"validUntil":null,"createdAt":"2026-04-08T03:54:52.528779563","updatedAt":"2026-04-08T03:54:52.52878644"}]}
186
Then status 201
0
187
* def tierBlock = response.tiers ? response.tiers[0] : response[0]
0
188
* def tierId = tierBlock.id ? tierBlock.id : tierBlock.tierId
0
189
* print 'Tier configured:', tierId
0
22:54:52.535 [print] Tier configured: f12125db-92fa-4de5-9aba-49c259687ea5
# Setup: Publish Event
192
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/publish'
0
193
And header X-Role = 'ADMIN'
0
194
When method patch
9
22:54:52.535 request: 4 > PATCH http://localhost:8081/api/v1/events/c0b9a716-a59a-43fd-a531-d9ed19523334/publish 4 > X-Role: ADMIN 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:54:52.544 response time in milliseconds: 8 4 < 200 4 < Content-Type: application/json 4 < Transfer-Encoding: chunked 4 < Date: Wed, 08 Apr 2026 03:54:51 GMT 4 < Keep-Alive: timeout=60 4 < Connection: keep-alive {"id":"c0b9a716-a59a-43fd-a531-d9ed19523334","roomId":"ec7fe579-55a7-4125-adc3-06b0259c72fc","title":"Karate Ticket Test Event 6cc7d637-3736-4d2c-a67d-27c72ed67a52","description":"Test event for declined payment","date":"2026-12-15T20:00:00","capacity":50,"status":"PUBLISHED","createdAt":"2026-04-08T03:54:52.517869","updatedAt":"2026-04-08T03:54:52.538199531","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}
195
Then status 200
0
196
* print 'Event published'
0
22:54:52.544 [print] Event published
# HU-08 Path: Create Reservation (Buyer 1)
199
Given url baseUrlTicketing + '/api/v1/reservations'
0
200
And header X-User-Id = buyerId1
0
201
And request
0
{ "eventId": "#(eventId)", "tierId": "#(tierId)", "buyerEmail": "#(buyerEmail1)" }
209
When method post
28
22:54:52.544 request: 5 > POST http://localhost:8082/api/v1/reservations 5 > X-User-Id: 6e8b348c-86e0-4d9e-9367-1ee11e9b624b 5 > Content-Type: application/json; charset=UTF-8 5 > Content-Length: 173 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":"c0b9a716-a59a-43fd-a531-d9ed19523334","tierId":"f12125db-92fa-4de5-9aba-49c259687ea5","buyerEmail":"buyer1-94ace10e-4281-48b1-b45a-c5298e7b0849@karate-test.com"} 22:54:52.572 response time in milliseconds: 28 5 < 201 5 < Content-Type: application/json 5 < Transfer-Encoding: chunked 5 < Date: Wed, 08 Apr 2026 03:54:51 GMT 5 < Keep-Alive: timeout=60 5 < Connection: keep-alive {"id":"285bba71-6286-4177-938b-fca0a2f82735","eventId":"c0b9a716-a59a-43fd-a531-d9ed19523334","tierId":"f12125db-92fa-4de5-9aba-49c259687ea5","buyerId":"6e8b348c-86e0-4d9e-9367-1ee11e9b624b","status":"PENDING","createdAt":"2026-04-08T03:54:52.566243028","updatedAt":"2026-04-08T03:54:52.566243028","validUntilAt":"2026-04-08T04:04:52.566243028"}
210
Then status 201
0
211
* def reservationId = response.id
0
212
* print 'Reservation created:', reservationId
0
22:54:52.573 [print] Reservation created: 285bba71-6286-4177-938b-fca0a2f82735
# HU-08 Path: Declined Payment
215
Given url baseUrlTicketing + '/api/v1/reservations/' + reservationId + '/payments'
0
216
And header X-User-Id = buyerId1
0
217
And request
0
{ "amount": 100, "paymentMethod": "MOCK", "status": "DECLINED" }
225
When method post
17
22:54:52.573 request: 6 > POST http://localhost:8082/api/v1/reservations/285bba71-6286-4177-938b-fca0a2f82735/payments 6 > X-User-Id: 6e8b348c-86e0-4d9e-9367-1ee11e9b624b 6 > Content-Type: application/json; charset=UTF-8 6 > Content-Length: 57 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":100,"paymentMethod":"MOCK","status":"DECLINED"} 22:54:52.590 response time in milliseconds: 17 6 < 400 6 < Content-Type: application/json 6 < Transfer-Encoding: chunked 6 < Date: Wed, 08 Apr 2026 03:54:51 GMT 6 < Connection: close {"reservationId":"285bba71-6286-4177-938b-fca0a2f82735","error":"Payment declined. Your reservation remains active for 10 minutes","status":"PAYMENT_FAILED","timestamp":"2026-04-08T03:54:52.589265012"}
226
Then status 400
0
# The API returns an error block, ensuring it does not have a valid ticket id
228
* def hasTicketId = response.ticketId != null && response.ticketId != ''
1
229
* assert !hasTicketId
0
230
* print 'Payment declined, no ticket generated (as expected)'
0
22:54:52.592 [print] Payment declined, no ticket generated (as expected)
Scenario: [3:233] Scenario 3 - Access Control (Buyer B Cannot Access Buyer A Ticket)
ms: 95
>>
Background:
7
* def baseUrlEvents = karate.properties['baseUrlEvents'] || 'http://localhost:8081'
0
22:54:52.593 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 buyerId1 = UUID.randomUUID() + ''
0
11
* def buyerId2 = UUID.randomUUID() + ''
0
12
* def buyerId3 = UUID.randomUUID() + ''
0
13
* def buyerEmail1 = 'buyer1-' + UUID.randomUUID() + '@karate-test.com'
0
14
* def buyerEmail2 = 'buyer2-' + UUID.randomUUID() + '@karate-test.com'
0
15
* def buyerEmail3 = 'buyer3-' + UUID.randomUUID() + '@karate-test.com'
0
16
* def eventTitle = 'Karate Ticket Test Event ' + UUID.randomUUID()
0
17
* def futureDate = '2026-12-15T20:00:00'
0
# Setup: Create Room
236
Given url baseUrlEvents + '/api/v1/rooms'
0
237
And header X-Role = 'ADMIN'
0
238
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
239
And request
0
{ "name": "Karate Test Room - Access Control", "maxCapacity": 100 }
246
When method post
8
22:54:52.594 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: 62 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":"Karate Test Room - Access Control","maxCapacity":100} 22:54:52.602 response time in milliseconds: 7 1 < 201 1 < Content-Type: application/json 1 < Transfer-Encoding: chunked 1 < Date: Wed, 08 Apr 2026 03:54:51 GMT 1 < Keep-Alive: timeout=60 1 < Connection: keep-alive {"id":"49f9d621-4378-4e61-b039-44a7c0733336","name":"Karate Test Room - Access Control","maxCapacity":100,"created_at":"2026-04-08T03:54:52.596766381","updated_at":"2026-04-08T03:54:52.596772569"}
247
Then status 201
0
248
* def roomId = response.id ? response.id : response.roomId
0
249
* print 'Room created:', roomId
0
22:54:52.603 [print] Room created: 49f9d621-4378-4e61-b039-44a7c0733336
# Setup: Create Draft Event
252
Given url baseUrlEvents + '/api/v1/events'
0
253
And header X-Role = 'ADMIN'
0
254
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
255
And request
0
{ "roomId": "#(roomId)", "title": "#(eventTitle)", "description": "Test event for access control", "date": "#(futureDate)", "capacity": 50, "enableSeats": false }
266
When method post
11
22:54:52.603 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: 230 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":"49f9d621-4378-4e61-b039-44a7c0733336","title":"Karate Ticket Test Event 9462a3c7-9489-4c8e-b9b6-2a20e1af9d25","description":"Test event for access control","date":"2026-12-15T20:00:00","capacity":50,"enableSeats":false} 22:54:52.614 response time in milliseconds: 10 2 < 201 2 < Content-Type: application/json 2 < Transfer-Encoding: chunked 2 < Date: Wed, 08 Apr 2026 03:54:51 GMT 2 < Keep-Alive: timeout=60 2 < Connection: keep-alive {"id":"75f48ad4-6644-4e9a-be56-cb46872eecf8","roomId":"49f9d621-4378-4e61-b039-44a7c0733336","title":"Karate Ticket Test Event 9462a3c7-9489-4c8e-b9b6-2a20e1af9d25","description":"Test event for access control","date":"2026-12-15T20:00:00","capacity":50,"status":"DRAFT","createdAt":"2026-04-08T03:54:52.607610975","updatedAt":"2026-04-08T03:54:52.607616573","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}
267
Then status 201
0
268
* def eventId = response.id ? response.id : response.eventId
0
269
* print 'Event created in DRAFT:', eventId
0
22:54:52.614 [print] Event created in DRAFT: 75f48ad4-6644-4e9a-be56-cb46872eecf8
# Setup: Configure Tier
272
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/tiers'
0
273
And header X-Role = 'ADMIN'
0
274
And request
0
[ { "tierType": "GENERAL", "price": 100, "quota": 40 } ]
284
When method post
10
22:54:52.615 request: 3 > POST http://localhost:8081/api/v1/events/75f48ad4-6644-4e9a-be56-cb46872eecf8/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":100,"quota":40}] 22:54:52.624 response time in milliseconds: 9 3 < 201 3 < Content-Type: application/json 3 < Transfer-Encoding: chunked 3 < Date: Wed, 08 Apr 2026 03:54:51 GMT 3 < Keep-Alive: timeout=60 3 < Connection: keep-alive {"eventId":"75f48ad4-6644-4e9a-be56-cb46872eecf8","tiers":[{"id":"9c95e3ee-aa07-4223-8044-00638b3e6334","tierType":"GENERAL","price":100,"quota":40,"validFrom":null,"validUntil":null,"createdAt":"2026-04-08T03:54:52.618224838","updatedAt":"2026-04-08T03:54:52.618242133"}]}
285
Then status 201
0
286
* def tierBlock = response.tiers ? response.tiers[0] : response[0]
0
287
* def tierId = tierBlock.id ? tierBlock.id : tierBlock.tierId
0
288
* print 'Tier configured:', tierId
0
22:54:52.624 [print] Tier configured: 9c95e3ee-aa07-4223-8044-00638b3e6334
# Setup: Publish Event
291
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/publish'
0
292
And header X-Role = 'ADMIN'
0
293
When method patch
9
22:54:52.625 request: 4 > PATCH http://localhost:8081/api/v1/events/75f48ad4-6644-4e9a-be56-cb46872eecf8/publish 4 > X-Role: ADMIN 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:54:52.633 response time in milliseconds: 8 4 < 200 4 < Content-Type: application/json 4 < Transfer-Encoding: chunked 4 < Date: Wed, 08 Apr 2026 03:54:51 GMT 4 < Keep-Alive: timeout=60 4 < Connection: keep-alive {"id":"75f48ad4-6644-4e9a-be56-cb46872eecf8","roomId":"49f9d621-4378-4e61-b039-44a7c0733336","title":"Karate Ticket Test Event 9462a3c7-9489-4c8e-b9b6-2a20e1af9d25","description":"Test event for access control","date":"2026-12-15T20:00:00","capacity":50,"status":"PUBLISHED","createdAt":"2026-04-08T03:54:52.607611","updatedAt":"2026-04-08T03:54:52.627841364","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}
294
Then status 200
0
295
* print 'Event published'
0
22:54:52.634 [print] Event published
# HU-09 Path: Buyer 1 creates Reservation and purchases ticket
298
Given url baseUrlTicketing + '/api/v1/reservations'
0
299
And header X-User-Id = buyerId1
0
300
And request
0
{ "eventId": "#(eventId)", "tierId": "#(tierId)", "buyerEmail": "#(buyerEmail1)" }
308
When method post
27
22:54:52.634 request: 5 > POST http://localhost:8082/api/v1/reservations 5 > X-User-Id: e389f95e-fe25-4f4e-ad85-0fedb370a7df 5 > Content-Type: application/json; charset=UTF-8 5 > Content-Length: 173 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":"75f48ad4-6644-4e9a-be56-cb46872eecf8","tierId":"9c95e3ee-aa07-4223-8044-00638b3e6334","buyerEmail":"buyer1-f8a2cb85-3b50-4389-93c8-fae427c7e513@karate-test.com"} 22:54:52.661 response time in milliseconds: 27 5 < 201 5 < Content-Type: application/json 5 < Transfer-Encoding: chunked 5 < Date: Wed, 08 Apr 2026 03:54:51 GMT 5 < Keep-Alive: timeout=60 5 < Connection: keep-alive {"id":"c6e30152-a276-4337-a421-5cf0e95cf5b3","eventId":"75f48ad4-6644-4e9a-be56-cb46872eecf8","tierId":"9c95e3ee-aa07-4223-8044-00638b3e6334","buyerId":"e389f95e-fe25-4f4e-ad85-0fedb370a7df","status":"PENDING","createdAt":"2026-04-08T03:54:52.654627463","updatedAt":"2026-04-08T03:54:52.654627463","validUntilAt":"2026-04-08T04:04:52.654627463"}
309
Then status 201
0
310
* def reservationId = response.id
0
311
* print 'Reservation created by Buyer 1:', reservationId
0
22:54:52.661 [print] Reservation created by Buyer 1: c6e30152-a276-4337-a421-5cf0e95cf5b3
# Buyer 1: Approved Payment
314
Given url baseUrlTicketing + '/api/v1/reservations/' + reservationId + '/payments'
0
315
And header X-User-Id = buyerId1
0
316
And request
0
{ "amount": 100, "paymentMethod": "MOCK", "status": "APPROVED" }
324
When method post
22
22:54:52.662 request: 6 > POST http://localhost:8082/api/v1/reservations/c6e30152-a276-4337-a421-5cf0e95cf5b3/payments 6 > X-User-Id: e389f95e-fe25-4f4e-ad85-0fedb370a7df 6 > Content-Type: application/json; charset=UTF-8 6 > Content-Length: 57 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":100,"paymentMethod":"MOCK","status":"APPROVED"} 22:54:52.683 response time in milliseconds: 21 6 < 200 6 < Content-Type: application/json 6 < Transfer-Encoding: chunked 6 < Date: Wed, 08 Apr 2026 03:54:51 GMT 6 < Keep-Alive: timeout=60 6 < Connection: keep-alive {"reservationId":"c6e30152-a276-4337-a421-5cf0e95cf5b3","status":"CONFIRMED","ticketId":"8a67f4d5-476b-4f05-9e4d-1712434fb7fc","message":"Payment approved. Ticket generated.","ticket":{"ticketId":"8a67f4d5-476b-4f05-9e4d-1712434fb7fc","eventId":"75f48ad4-6644-4e9a-be56-cb46872eecf8","eventTitle":"Karate Ticket Test Event 9462a3c7-9489-4c8e-b9b6-2a20e1af9d25","eventDate":"2026-12-15T20:00:00","tier":"GENERAL","pricePaid":100,"status":"VALID","purchasedAt":"2026-04-08T03:54:52.671762979","buyerEmail":"buyer1-f8a2cb85-3b50-4389-93c8-fae427c7e513@karate-test.com","reservationId":"c6e30152-a276-4337-a421-5cf0e95cf5b3"},"timestamp":"2026-04-08T03:54:52.676799019"}
325
Then status 200
0
326
* def ticketId = response.ticketId
0
327
* print 'Buyer 1 purchased ticket:', ticketId
0
22:54:52.684 [print] Buyer 1 purchased ticket: 8a67f4d5-476b-4f05-9e4d-1712434fb7fc
# HU-09 Path: Buyer 2 attempts to access Buyer 1's ticket
330
Given url baseUrlTicketing + '/api/v1/tickets/' + ticketId
0
331
And header X-User-Id = buyerId2
0
332
When method get
5
22:54:52.684 request: 7 > GET http://localhost:8082/api/v1/tickets/8a67f4d5-476b-4f05-9e4d-1712434fb7fc 7 > X-User-Id: f973ece5-6219-4e80-9fb4-e7fd03386eee 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 22:54:52.689 response time in milliseconds: 4 7 < 403 7 < Content-Type: application/json 7 < Transfer-Encoding: chunked 7 < Date: Wed, 08 Apr 2026 03:54:51 GMT 7 < Keep-Alive: timeout=60 7 < Connection: keep-alive {"error":"You can only view your own tickets","timestamp":"2026-04-08T03:54:52.688134242"}
333
Then status 403
0
334
* print 'Buyer 2 access denied (403) - ticket belongs to Buyer 1'
0
22:54:52.689 [print] Buyer 2 access denied (403) - ticket belongs to Buyer 1
Scenario: [4:337] Scenario 4 - No ticket shown for buyer when reservation expires without payment
ms: 65123
>>
Background:
7
* def baseUrlEvents = karate.properties['baseUrlEvents'] || 'http://localhost:8081'
0
22:54:52.690 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 buyerId1 = UUID.randomUUID() + ''
0
11
* def buyerId2 = UUID.randomUUID() + ''
0
12
* def buyerId3 = UUID.randomUUID() + ''
0
13
* def buyerEmail1 = 'buyer1-' + UUID.randomUUID() + '@karate-test.com'
0
14
* def buyerEmail2 = 'buyer2-' + UUID.randomUUID() + '@karate-test.com'
0
15
* def buyerEmail3 = 'buyer3-' + UUID.randomUUID() + '@karate-test.com'
0
16
* def eventTitle = 'Karate Ticket Test Event ' + UUID.randomUUID()
0
17
* def futureDate = '2026-12-15T20:00:00'
0
# Setup: Create Room
340
Given url baseUrlEvents + '/api/v1/rooms'
0
341
And header X-Role = 'ADMIN'
0
342
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
343
And request
0
{ "name": "Karate Test Room - Expired Ticket Check", "maxCapacity": 100 }
350
When method post
9
22:54:52.692 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: 68 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":"Karate Test Room - Expired Ticket Check","maxCapacity":100} 22:54:52.700 response time in milliseconds: 8 1 < 201 1 < Content-Type: application/json 1 < Transfer-Encoding: chunked 1 < Date: Wed, 08 Apr 2026 03:54:51 GMT 1 < Keep-Alive: timeout=60 1 < Connection: keep-alive {"id":"623bf8cb-0df8-49ae-999e-1b11803da15d","name":"Karate Test Room - Expired Ticket Check","maxCapacity":100,"created_at":"2026-04-08T03:54:52.694395318","updated_at":"2026-04-08T03:54:52.694404819"}
351
Then status 201
0
352
* def roomId = response.id ? response.id : response.roomId
0
# Setup: Create Draft Event
355
Given url baseUrlEvents + '/api/v1/events'
0
356
And header X-Role = 'ADMIN'
0
357
And header X-User-Id = '00000000-0000-0000-0000-000000000001'
0
358
And request
0
{ "roomId": "#(roomId)", "title": "#(eventTitle + ' Expiration')", "description": "Test no ticket generated when expired", "date": "#(futureDate)", "capacity": 50, "enableSeats": false }
369
When method post
12
22:54:52.701 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: 249 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":"623bf8cb-0df8-49ae-999e-1b11803da15d","title":"Karate Ticket Test Event b204d18a-388d-4409-b4a5-71e58be74bfb Expiration","description":"Test no ticket generated when expired","date":"2026-12-15T20:00:00","capacity":50,"enableSeats":false} 22:54:52.712 response time in milliseconds: 11 2 < 201 2 < Content-Type: application/json 2 < Transfer-Encoding: chunked 2 < Date: Wed, 08 Apr 2026 03:54:51 GMT 2 < Keep-Alive: timeout=60 2 < Connection: keep-alive {"id":"8a58f359-e01b-4a88-a492-03a31ef0828e","roomId":"623bf8cb-0df8-49ae-999e-1b11803da15d","title":"Karate Ticket Test Event b204d18a-388d-4409-b4a5-71e58be74bfb Expiration","description":"Test no ticket generated when expired","date":"2026-12-15T20:00:00","capacity":50,"status":"DRAFT","createdAt":"2026-04-08T03:54:52.706513366","updatedAt":"2026-04-08T03:54:52.706517526","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}
370
Then status 201
0
371
* def eventId = response.id ? response.id : response.eventId
0
# Setup: Configure Tier
374
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/tiers'
0
375
And header X-Role = 'ADMIN'
0
376
And request
0
[ { "tierType": "GENERAL", "price": 100, "quota": 40 } ]
386
When method post
10
22:54:52.713 request: 3 > POST http://localhost:8081/api/v1/events/8a58f359-e01b-4a88-a492-03a31ef0828e/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":100,"quota":40}] 22:54:52.722 response time in milliseconds: 9 3 < 201 3 < Content-Type: application/json 3 < Transfer-Encoding: chunked 3 < Date: Wed, 08 Apr 2026 03:54:51 GMT 3 < Keep-Alive: timeout=60 3 < Connection: keep-alive {"eventId":"8a58f359-e01b-4a88-a492-03a31ef0828e","tiers":[{"id":"0052d21a-6d6b-479e-bf09-8abdc5307311","tierType":"GENERAL","price":100,"quota":40,"validFrom":null,"validUntil":null,"createdAt":"2026-04-08T03:54:52.716886397","updatedAt":"2026-04-08T03:54:52.716891664"}]}
387
Then status 201
0
388
* def tierBlock = response.tiers ? response.tiers[0] : response[0]
0
389
* def tierId = tierBlock.id ? tierBlock.id : tierBlock.tierId
0
# Setup: Publish Event
392
Given url baseUrlEvents + '/api/v1/events/' + eventId + '/publish'
0
393
And header X-Role = 'ADMIN'
0
394
When method patch
11
22:54:52.723 request: 4 > PATCH http://localhost:8081/api/v1/events/8a58f359-e01b-4a88-a492-03a31ef0828e/publish 4 > X-Role: ADMIN 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:54:52.734 response time in milliseconds: 11 4 < 200 4 < Content-Type: application/json 4 < Transfer-Encoding: chunked 4 < Date: Wed, 08 Apr 2026 03:54:51 GMT 4 < Keep-Alive: timeout=60 4 < Connection: keep-alive {"id":"8a58f359-e01b-4a88-a492-03a31ef0828e","roomId":"623bf8cb-0df8-49ae-999e-1b11803da15d","title":"Karate Ticket Test Event b204d18a-388d-4409-b4a5-71e58be74bfb Expiration","description":"Test no ticket generated when expired","date":"2026-12-15T20:00:00","capacity":50,"status":"PUBLISHED","createdAt":"2026-04-08T03:54:52.706513","updatedAt":"2026-04-08T03:54:52.727850759","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}
395
Then status 200
0
# Path B: Create Reservation for Buyer 3
398
Given url baseUrlTicketing + '/api/v1/reservations'
0
399
And header X-User-Id = buyerId3
0
400
And request
0
{ "eventId": "#(eventId)", "tierId": "#(tierId)", "buyerEmail": "#(buyerEmail3)" }
408
When method post
26
22:54:52.735 request: 5 > POST http://localhost:8082/api/v1/reservations 5 > X-User-Id: 1375b3fc-0322-4d84-8be4-87a4b91c4070 5 > Content-Type: application/json; charset=UTF-8 5 > Content-Length: 173 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":"8a58f359-e01b-4a88-a492-03a31ef0828e","tierId":"0052d21a-6d6b-479e-bf09-8abdc5307311","buyerEmail":"buyer3-4188eae2-c978-43a9-9d77-64ae617b3d01@karate-test.com"} 22:54:52.760 response time in milliseconds: 24 5 < 201 5 < Content-Type: application/json 5 < Transfer-Encoding: chunked 5 < Date: Wed, 08 Apr 2026 03:54:51 GMT 5 < Keep-Alive: timeout=60 5 < Connection: keep-alive {"id":"69b33ca8-6aa4-48c4-90dd-c74b114748ea","eventId":"8a58f359-e01b-4a88-a492-03a31ef0828e","tierId":"0052d21a-6d6b-479e-bf09-8abdc5307311","buyerId":"1375b3fc-0322-4d84-8be4-87a4b91c4070","status":"PENDING","createdAt":"2026-04-08T03:54:52.75312375","updatedAt":"2026-04-08T03:54:52.75312375","validUntilAt":"2026-04-08T04:04:52.75312375"}
409
Then status 201
0
410
* def reservationId = response.id
0
411
* print 'Reservation created for Buyer 3 (PENDING):', reservationId
1
22:54:52.760 [print] Reservation created for Buyer 3 (PENDING): 69b33ca8-6aa4-48c4-90dd-c74b114748ea
# Path B: Force expiration using SQL mechanism to avoid 10 min hard sleep
414
* print 'Forcing expiration via DB timeout mechanism...'
0
22:54:52.761 [print] Forcing expiration via DB timeout mechanism...
415
* def sqlResult = karate.call('classpath:common/sql/db-helper.feature@forceExpiration', { reservationId: reservationId })
17
>>
common.sql.db-helper
13
5
* def dbTicketingUrl = karate.properties['dbTicketingUrl'] || 'jdbc:postgresql://localhost:5434/ticketing_db'
0
6
* def dbTicketingUser = karate.properties['dbTicketingUser'] || 'postgres'
0
7
* def dbTicketingPass = karate.properties['dbTicketingPass'] || 'postgres'
0
9
* def dbEventsUrl = karate.properties['dbEventsUrl'] || 'jdbc:postgresql://localhost:5433/events_db'
0
10
* def dbEventsUser = karate.properties['dbEventsUser'] || 'postgres'
0
11
* def dbEventsPass = karate.properties['dbEventsPass'] || 'postgres'
0
13
* def JClass = Java.type('java.lang.Class')
0
14
* def DriverManager = Java.type('java.sql.DriverManager')
0
15
* JClass.forName('org.postgresql.Driver')
0
19
* def sql = "UPDATE reservation SET valid_until_at = NOW() AT TIME ZONE 'UTC' - INTERVAL '2 days' WHERE id = ?::uuid"
0
20
* def conn = DriverManager.getConnection(dbTicketingUrl, dbTicketingUser, dbTicketingPass)
5
21
* def pstmt = conn.prepareStatement(sql)
0
22
* pstmt.setObject(1, reservationId)
0
23
* def rows = pstmt.executeUpdate()
6
24
* pstmt.close()
0
25
* conn.close()
0
26
* print 'SQL Result: Forced expiration for reservationId=' + reservationId + ', Rows updated=' + rows
0
22:54:52.777 [print] SQL Result: Forced expiration for reservationId=69b33ca8-6aa4-48c4-90dd-c74b114748ea, Rows updated=1
27
* eval if (rows != 1) karate.fail('Expected exactly 1 reservation row to be updated, but updated ' + rows)
0
28
* def response = { rowsUpdated: rows, passed: true }
0
416
* print 'Waiting 65s for backend scheduler to clean up...'
0
22:54:52.777 [print] Waiting 65s for backend scheduler to clean up...
417
* java.lang.Thread.sleep(65000)
65001
# Path B: Check reservation is truly expired
420
* def checkResult = karate.call('classpath:common/sql/db-helper.feature@checkReservationStatus', { reservationId: reservationId, expectedStatus: 'EXPIRED' })
22
>>
common.sql.db-helper
13
5
* def dbTicketingUrl = karate.properties['dbTicketingUrl'] || 'jdbc:postgresql://localhost:5434/ticketing_db'
0
6
* def dbTicketingUser = karate.properties['dbTicketingUser'] || 'postgres'
0
7
* def dbTicketingPass = karate.properties['dbTicketingPass'] || 'postgres'
0
9
* def dbEventsUrl = karate.properties['dbEventsUrl'] || 'jdbc:postgresql://localhost:5433/events_db'
0
10
* def dbEventsUser = karate.properties['dbEventsUser'] || 'postgres'
0
11
* def dbEventsPass = karate.properties['dbEventsPass'] || 'postgres'
0
13
* def JClass = Java.type('java.lang.Class')
0
14
* def DriverManager = Java.type('java.sql.DriverManager')
0
15
* JClass.forName('org.postgresql.Driver')
0
32
* def sql = 'SELECT status, updated_at FROM reservation WHERE id = ?::uuid ORDER BY updated_at DESC LIMIT 1'
0
33
* def conn = DriverManager.getConnection(dbTicketingUrl, dbTicketingUser, dbTicketingPass)
8
34
* def pstmt = conn.prepareStatement(sql)
0
35
* pstmt.setObject(1, reservationId)
0
36
* def rs = pstmt.executeQuery()
2
38
* def found = rs.next()
0
39
* eval if (!found) karate.fail('Reservation not found in database for id=' + reservationId)
0
41
* def actualStatus = rs.getString('status')
0
42
* def updatedAt = rs.getString('updated_at')
0
44
* rs.close()
0
45
* pstmt.close()
0
46
* conn.close()
0
48
* print 'SQL Result: reservationId=' + reservationId + ', status=' + actualStatus + ', updated_at=' + updatedAt
0
22:55:57.800 [print] SQL Result: reservationId=69b33ca8-6aa4-48c4-90dd-c74b114748ea, status=EXPIRED, updated_at=2026-04-08 03:55:35.638377
49
* eval if (actualStatus != expectedStatus) karate.fail('Expected status ' + expectedStatus + ' but found ' + actualStatus)
0
51
* def response = { actualStatus: actualStatus, updatedAt: updatedAt, passed: true }
0
421
* print 'Reservation successfully expired in backend.'
0
22:55:57.801 [print] Reservation successfully expired in backend.
# Act: Buyer 3 fetches their tickets
424
Given url baseUrlTicketing + '/api/v1/tickets'
0
425
And param buyerId = buyerId3
0
426
And header X-User-Id = buyerId3
0
427
When method get
12
22:55:57.802 request: 6 > GET http://localhost:8082/api/v1/tickets?buyerId=1375b3fc-0322-4d84-8be4-87a4b91c4070 6 > X-User-Id: 1375b3fc-0322-4d84-8be4-87a4b91c4070 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 22:55:57.813 response time in milliseconds: 11 6 < 200 6 < Content-Type: application/json 6 < Transfer-Encoding: chunked 6 < Date: Wed, 08 Apr 2026 03:55:57 GMT 6 < Keep-Alive: timeout=60 6 < Connection: keep-alive {"content":[],"page":0,"size":10,"totalElements":0,"totalPages":0}
428
Then status 200
0
429
* print 'Tickets retrieved for Buyer 3:', response
0
22:55:57.814 [print] Tickets retrieved for Buyer 3: { "content": [ ], "page": 0, "size": 10, "totalElements": 0, "totalPages": 0 }
# Assertion: Buyer 3 should have NO tickets associated (empty content array)
432
* match response.content == []
0
433
* match response.totalElements == 0
0
434
* print 'Verified: Buyer 3 has no tickets. TC-025 passed!'
0
22:55:57.814 [print] Verified: Buyer 3 has no tickets. TC-025 passed!