GoGlobal Integration - Testing Guide¶
This document describes the testing infrastructure for the GoGlobal hotel integration plugin (tqgglbl), including the API simulator, test architecture, and how to run and extend the test suite.
Overview¶
The GoGlobal integration is tested at five levels:
| Level | Test Class | Scope | Dependencies |
|---|---|---|---|
| DB Integration | GoGlobalDBIntegrationTest |
CRUD on goglobal schema tables |
PostgreSQL |
| Facade Mapping | GoGlobalFacadeTest |
DTO-to-canonical entity mapping | Mockito |
| SOAP Client | GoGlobalSoapClientTest |
SOAP envelope construction and parsing | Mockito, fixtures |
| Request Builder | GoGlobalRequestBuilderTest |
XML request generation per XSD schemas | None (unit) |
| E2E Integration | GoGlobalE2ETest |
Full booking lifecycle via API simulator | GoGlobal Simulator |
The E2E tests use an embedded HTTP server (the GoGlobal API Simulator) that replaces the real GoGlobal API, enabling deterministic testing of the complete request-response flow without external dependencies.
GoGlobal API Simulator¶
Purpose¶
The simulator is a self-contained HTTP server that implements the GoGlobal SOAP 1.2 API. It accepts the same requests that GoGlobalSoapClient sends to the real API, validates inputs per GoGlobal specification rules, maintains stateful session and booking data, and returns syntactically and semantically correct responses.
Architecture¶
GoGlobalSimulator
│
├── GoGlobalSoapHandler (HttpHandler)
│ Accepts POST, reads API-Operation header,
│ extracts inner XML from SOAP CDATA,
│ routes to operation handler,
│ wraps response in SOAP envelope
│ │
│ └── OperationHandler (interface)
│ ├── AvailabilityHandler (Op 11 → JSON)
│ ├── ValuationHandler (Op 9 → XML)
│ ├── BookingHandler (Op 2 → XML)
│ ├── BookingStatusHandler (Op 5 → XML)
│ ├── BookingDetailsHandler (Op 4 → XML)
│ ├── CancellationHandler (Op 3 → XML)
│ ├── HotelInfoHandler (Op 6/61 → XML)
│ ├── PriceBreakdownHandler (Op 14 → XML)
│ ├── AmendmentOptionsHandler (Op 15 → XML)
│ ├── AmendmentRequestHandler (Op 16 → XML)
│ ├── VoucherHandler (Op 8 → XML)
│ └── AdvancedSearchHandler (Op 10 → XML)
│
├── SimulatorState (thread-safe session + booking storage)
├── SimulatorHotelCatalog (5 static test hotels)
├── SimulatorPricingEngine (deterministic pricing from seed)
├── SimulatorResponseBuilder (SOAP envelope wrapping)
├── SimulatorRequestParser (SOAP CDATA extraction + DOM parsing)
└── SimulatorConfig (timeouts, seed, credentials, logging)
Location¶
All simulator classes are under:
Quick Start¶
// 1. Create configuration
SimulatorConfig config = new SimulatorConfig()
.withSessionExpiryMs(5 * 60 * 1000L) // 5 min for tests
.withSeed(42L) // deterministic results
.withLogging(true); // log requests/responses
// 2. Start simulator on dynamic port
GoGlobalSimulator simulator = new GoGlobalSimulator(config);
int port = simulator.start();
// 3. Point GoGlobalClientConfig to the simulator
GoGlobalClientConfig.resetInstance();
GoGlobalClientConfig.instance().setProp(
"goglobal.api.url", simulator.getBaseUrl());
// 4. Use real facade — requests go through SOAP client to simulator
GoGlobalHotelFacade facade = GoGlobalHotelFacade.getInstance();
List<CHotelOffer> offers = facade.searchOffers(search);
// 5. Stop when done
simulator.stop();
GoGlobalClientConfig.resetInstance();
The simulator binds to port 0 (OS-assigned dynamic port), eliminating port conflicts in CI environments.
Simulator Components¶
SimulatorConfig¶
Controls simulator behavior. All settings have sensible defaults.
| Property | Default | Description |
|---|---|---|
sessionExpiryMs |
1,200,000 (20 min) | How long a HotelSearchCode remains valid |
seed |
42 | Seed for deterministic pricing and status generation |
agencyId |
"TESTAGENCY" |
Expected agency ID in requests |
login |
"testuser" |
Expected username |
password |
"testpass" |
Expected password |
logRequests |
true |
Log incoming SOAP requests to stdout |
logResponses |
true |
Log outgoing responses to stdout |
All properties support fluent builder methods (withXxx()).
SimulatorState¶
Thread-safe storage using ConcurrentHashMap for:
- Search sessions — keyed by HotelSearchCode, storing search parameters and generated offers
- Bookings — keyed by GoBookingCode, storing full booking lifecycle state
Key methods for test assertions and manipulation:
// Check state after operations
SimulatedBooking booking = simulator.getState().getBooking("GBC_SIM_1001");
assertEquals("C", booking.getStatus());
// Force session expiry for testing
simulator.getState().backdateSession("HSC_SIM_42", 21 * 60 * 1000L);
// Reset all state between test groups
simulator.reset();
SimulatorHotelCatalog¶
Static catalog of 5 test hotels, all in city code "1234" (Dubai):
| Hotel ID | Name | Stars | Coordinates |
|---|---|---|---|
| 54321 | Dubai Marina Resort & Spa | 5 | 25.08°N, 55.14°E |
| 54322 | Palm Beach Hotel | 5 | 25.11°N, 55.13°E |
| 54323 | Downtown Grand Hotel | 4 | 25.20°N, 55.27°E |
| 54324 | Creek Heritage Inn | 3 | 25.26°N, 55.30°E |
| 54325 | JBR Seaside Apartments | 4 | 25.08°N, 55.13°E |
Each hotel has realistic facilities (pool, gym, restaurant, etc.), room facilities (AC, TV, minibar), and contact details. Searching any city code other than "1234" returns error 209.
SimulatorPricingEngine¶
Generates deterministic prices from the seed and hotel attributes. No randomness — identical inputs always produce identical outputs.
Nightly rate formula:
base = (stars × 50) + 50 → 3★=$200, 4★=$250, 5★=$300
variance = ((hotelId × 31 + nightIndex × 17) % 50) - 25
rate = base + variance
Other calculations: - Total price = sum of nightly rates × room count - Tax = 10% of total - Commission = 10-20% (deterministic per hotel) - Offers per hotel = 2-5 (deterministic per hotelId) - Booking status = ~90% Confirmed, ~8% On Request, ~2% Rejected (deterministic per HotelSearchCode hash)
SimulatorResponseBuilder¶
Wraps handler output in the exact SOAP 1.2 envelope format that GoGlobalSoapClient.extractResponseContent() expects:
The response content is HTML-entity-encoded (< → <, > → >) inside <MakeRequestResult>, matching GoGlobal's real API behavior. The production GoGlobalSoapClient decodes this automatically.
Input Validation and Error Codes¶
The simulator enforces GoGlobal specification validation rules and returns proper error codes:
Availability Search (Op 11)¶
| Rule | Error Code | Description |
|---|---|---|
| Arrival date format | 208 | Must be valid yyyy-MM-dd |
| Arrival date in future | 208 | Cannot search for past dates |
| City code exists | 209 | Must match a city in the hotel catalog |
| Nights range | 210 | Must be 1-99 |
| Room count | 214 | Maximum 8 rooms per search |
| Pax per room | 143 | Maximum 8 occupants (adults + children) per room |
| Children per room | 211 | Maximum 4 children per room |
Booking Creation (Op 2)¶
| Rule | Error Code | Description |
|---|---|---|
| HotelSearchCode valid | 312 | Must exist and not be expired (default 20 min) |
| Pax title | 314 | Must be MR., MRS., MISS, or MS. (case-sensitive, with period) |
| First name | 314 | Must start with a letter; ASCII letters, spaces, hyphens, apostrophes only |
| Last name | 314 | Minimum 2 characters; same character rules as first name |
Cancellation (Op 3)¶
| Rule | Error Code | Description |
|---|---|---|
| Booking exists | 401 | GoBookingCode must be in state |
| Booking cancellable | 402 | Cannot cancel if already cancelled (X/XP) or rejected (RJ) |
Voucher (Op 8)¶
| Rule | Error Code | Description |
|---|---|---|
| Booking confirmed | 700 | Status must be C; cannot generate voucher for RQ, X, or RJ |
Amendment (Op 15)¶
| Rule | Error Code | Description |
|---|---|---|
| Booking exists | 401 | GoBookingCode must be in state |
| Booking amendable | 704 | Status must be C to allow amendments |
Cancellation Logic¶
The simulator determines cancellation outcome based on the booking's cancellation deadline and refundability:
Non-refundable booking:
→ Status XP (Cancelled with Penalty), fee = 100% of total price
Before cancellation deadline:
→ Status X (Cancelled Free), fee = 0
After cancellation deadline:
→ Status XP (Cancelled with Penalty), fee = 50% of total price
The cancellation deadline is set to arrival date minus 3 days when the booking is created.
Booking Status Lifecycle¶
The simulator tracks booking status transitions:
┌──────────┐
Op 2 (Book) ──→ │ C │ ──→ Op 8 (Voucher)
│Confirmed │ ──→ Op 15/16 (Amend)
└────┬─────┘
│ Op 3 (Cancel)
┌────┴─────┐
Before CXL │ X │ Free cancellation
deadline └──────────┘
┌──────────┐
After CXL │ XP │ Penalty cancellation
deadline └──────────┘
Op 2 (Book) ──→ │ RQ │ ──→ Poll Op 5 until C or RJ
│On Request│
Op 2 (Book) ──→ │ RJ │ (terminal state)
│ Rejected │
Test Suite¶
E2E Integration Tests (GoGlobalE2ETest)¶
The primary test class exercises the full booking lifecycle through the simulator. Tests are ordered (@Order) and share state across the class (@TestInstance(PER_CLASS)).
Happy Path Flow (UC-11.1): 1. Search hotel offers (Op 11) → verify offers returned with HotelSearchCode 2. Get hotel info (Op 6) → verify hotel details and facilities 3. Get price breakdown (Op 14) → verify per-night pricing 4. Valuate offer (Op 9) → verify confirmed pricing and CXL deadline 5. Create booking (Op 2) → verify GoBookingCode and status 6. Check booking status (Op 5) → verify status matches 7. Get booking details (Op 4) → verify full booking data 8. Generate voucher (Op 8) → verify voucher URL
Cancellation Flows: - Free cancellation — book, cancel before deadline → status X - Penalty cancellation — book non-refundable, cancel → status XP
Amendment Flow: - Book → get amendment options (Op 15) → submit amendment (Op 16)
Dual-Mode Routing:
- searchMode=online → routes to GoGlobal simulator
- searchMode=contracted → routes to NTS (contracted hotels)
- Default mode is online
Error Scenarios: - Invalid city code → error 209 - Excessive nights (>99) → error 210 - Too many pax per room (>8) → error 143 - Invalid pax names → error 314 - Expired search code → error 312 - Cancel non-existent booking → error 401 - Voucher on unconfirmed booking → error 700
Input Validation: - Pax name character restrictions (non-Unicode, letter-first) - Title validation (MR./MRS./MISS/MS.) - Child title (CHD) - Date format (yyyy-MM-dd) - Nationality (2-letter ISO) - Room configuration limits
Session Expiry:
- Uses simulator.getState().backdateSession() to test expired search code handling
Other Test Levels¶
| Test Class | Tests | What It Tests | Mock Strategy |
|---|---|---|---|
GoGlobalSoapClientTest |
~43 | SOAP envelope construction, JSON/XML parsing, error handling, timeouts | Mockito on HttpClient |
GoGlobalRequestBuilderTest |
~41 | XML request generation for all 12 operations against XSD schemas | None (pure unit tests) |
GoGlobalFacadeTest |
~11 | DTO-to-canonical entity field mapping (GGAvailabilityResponse → CHotelOffer, etc.) | Mockito on SoapClient + ResponseParser |
GoGlobalDBIntegrationTest |
~18 | CRUD on goglobal schema tables (country, city, hotel, star_rating, refresh_log) | PostgreSQL required |
Test Resources¶
Static fixture files in tqgglbl/src/test/resources/goglobal/:
| File | Format | Used By |
|---|---|---|
availability_response.json |
JSON | SoapClientTest, FacadeTest |
valuation_response.xml |
XML | SoapClientTest, FacadeTest |
booking_response.xml |
XML | SoapClientTest, FacadeTest |
booking_status_response.xml |
XML | SoapClientTest |
booking_details_response.xml |
XML | SoapClientTest |
hotel_info_response.xml |
XML | SoapClientTest |
price_breakdown_response.xml |
XML | SoapClientTest |
voucher_response.xml |
XML | SoapClientTest |
cancellation_free_response.xml |
XML | SoapClientTest |
cancellation_penalty_response.xml |
XML | SoapClientTest |
amendment_options_response.xml |
XML | SoapClientTest |
amendment_request_response.xml |
XML | SoapClientTest |
error_209_invalid_city.xml |
XML | SoapClientTest |
error_210_invalid_nights.xml |
XML | SoapClientTest |
error_312_expired_code.xml |
XML | SoapClientTest |
error_401_booking_not_found.xml |
XML | SoapClientTest |
error_700_voucher_unavailable.xml |
XML | SoapClientTest |
These fixtures are used by the SOAP client and facade unit tests (Mockito-based). The E2E tests do not use fixture files — they receive live responses from the simulator.
Running Tests¶
Prerequisites¶
- Java 17+
- PostgreSQL with
goglobalschema (for DB integration tests only) TLINQ_HOMEenvironment variable pointing toconfig/directory (set automatically by tests)
Commands¶
# Build all modules (tests skipped by default)
./gradlew build
# Run all tqgglbl tests
./gradlew :tqgglbl:test
# Run only E2E tests (simulator-based)
./gradlew :tqgglbl:test --tests "com.perun.tlinq.client.goglobal.GoGlobalE2ETest"
# Run only DB integration tests
./gradlew :tqgglbl:test --tests "com.perun.tlinq.client.goglobal.db.GoGlobalDBIntegrationTest"
# Run only SOAP client unit tests
./gradlew :tqgglbl:test --tests "com.perun.tlinq.client.goglobal.remote.GoGlobalSoapClientTest"
# Run only request builder unit tests
./gradlew :tqgglbl:test --tests "com.perun.tlinq.client.goglobal.remote.GoGlobalRequestBuilderTest"
# Run only facade mapping tests
./gradlew :tqgglbl:test --tests "com.perun.tlinq.client.goglobal.service.GoGlobalFacadeTest"
Troubleshooting¶
Port conflicts: The simulator uses port 0 (OS-assigned). If tests fail with BindException, ensure no other process holds the assigned port.
Session expiry in tests: If booking tests fail with error 312 (expired search code), check that the SimulatorConfig.sessionExpiryMs is long enough for the test execution time. Default is 5 minutes in E2E tests.
Simulator logging: Set SimulatorConfig.withLogging(true) to see all SOAP requests and responses in the test output. Look for [SIM-REQ] and [SIM-RSP] log prefixes.
TLINQ_HOME not found: Tests auto-detect the config directory by walking up from the test class location. If this fails, set the TLINQ_HOME environment variable to point to tqpro/config/.
Extending the Simulator¶
Adding a New Operation Handler¶
-
Create a new class implementing
OperationHandler:package com.perun.tlinq.client.goglobal.simulator; public class NewOperationHandler implements OperationHandler { @Override public String handle(int opCode, String innerXml, SimulatorState state, SimulatorConfig config) { // Parse innerXml // Validate inputs // Update state if needed // Return XML response (or JSON for availability) return SimulatorResponseBuilder.xmlHeader(opCode) + "<NewField>value</NewField>" + SimulatorResponseBuilder.xmlFooter(); } } -
Register it in
GoGlobalSoapHandler.registerHandlers():
Adding a New Hotel to the Catalog¶
Add a new CatalogHotel entry to SimulatorHotelCatalog.HOTELS:
new CatalogHotel(54326, "New Hotel Name", "1234", "Dubai",
"4", 25.15, 55.20, "Address", "+971 4 555 6789", "AE",
List.of("Pool", "Gym"), List.of("AC", "TV"))
Adding a New City¶
The current catalog only supports city "1234" (Dubai). To add more cities, add hotels with different cityCode values to SimulatorHotelCatalog.HOTELS. The getHotelsByCity() method automatically filters by city code.
Testing Session Expiry¶
Use backdateSession() to artificially age a search session:
// Create a search
List<CHotelOffer> offers = facade.searchOffers(search);
String hsc = offers.get(0).getOfferId();
// Simulate 21 minutes passing
simulator.getState().backdateSession(hsc, 21 * 60 * 1000L);
// Now valuation/booking should fail with error 312
assertThrows(TlinqClientException.class, () -> facade.valuateOffer(hsc));
Customizing Pricing¶
The SimulatorPricingEngine uses a seed for deterministic results. Change the seed in SimulatorConfig to get different (but still deterministic) price points:
Transitioning to the Real Testbed¶
Once the simulator-based tests pass, switching to the GoGlobal sandbox is straightforward:
-
Update
config/goglobal-client.xmlwith real credentials: -
Remove the simulator override in the E2E test
@BeforeAll(or create a separate test class for live integration). -
Note that live tests will produce non-deterministic results (real hotel availability varies) — assertions should be relaxed to check structure and types rather than exact values.
File Reference¶
Simulator Classes¶
| Class | Purpose |
|---|---|
GoGlobalSimulator |
HTTP server lifecycle (start/stop/getBaseUrl) |
GoGlobalSoapHandler |
HTTP request handler, operation routing, SOAP wrapping |
OperationHandler |
Interface for operation-specific handlers |
AvailabilityHandler |
Op 11: search validation, JSON offer generation |
ValuationHandler |
Op 9: pricing confirmation |
BookingHandler |
Op 2: pax validation, booking creation |
BookingStatusHandler |
Op 5: status lookup |
BookingDetailsHandler |
Op 4: full booking details |
CancellationHandler |
Op 3: cancellation with deadline logic |
HotelInfoHandler |
Op 6/61: hotel catalog lookup |
PriceBreakdownHandler |
Op 14: per-night pricing |
AmendmentOptionsHandler |
Op 15: amendable fields |
AmendmentRequestHandler |
Op 16: apply amendments |
VoucherHandler |
Op 8: voucher generation |
AdvancedSearchHandler |
Op 10: booking search by criteria |
SimulatorConfig |
Configuration with builder pattern |
SimulatorState |
Thread-safe session + booking storage |
SearchSession |
Search session POJO with inner RoomConfig and GeneratedOffer |
SimulatedBooking |
Booking lifecycle POJO |
SimulatorHotelCatalog |
Static hotel catalog (5 hotels) |
SimulatorPricingEngine |
Deterministic pricing from seed |
SimulatorResponseBuilder |
SOAP envelope construction |
SimulatorRequestParser |
SOAP CDATA extraction and DOM parsing |
Test Classes¶
| Class | Level | Tests | Strategy |
|---|---|---|---|
GoGlobalE2ETest |
E2E | ~86 | Live simulator |
GoGlobalSoapClientTest |
Unit | ~43 | Mockito on HttpClient |
GoGlobalRequestBuilderTest |
Unit | ~41 | Pure unit |
GoGlobalFacadeTest |
Unit | ~11 | Mockito on SoapClient |
GoGlobalDBIntegrationTest |
Integration | ~18 | PostgreSQL |