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!