Skip to content

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:

tqgglbl/src/test/java/com/perun/tlinq/client/goglobal/simulator/

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:

SOAP Envelope → MakeRequestResponse → MakeRequestResult
  └── HTML-entity-encoded response content

The response content is HTML-entity-encoded (<&lt;, >&gt;) 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 goglobal schema (for DB integration tests only)
  • TLINQ_HOME environment variable pointing to config/ 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

  1. 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();
        }
    }
    

  2. Register it in GoGlobalSoapHandler.registerHandlers():

    handlers.put(99, new NewOperationHandler());
    

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:

SimulatorConfig config = new SimulatorConfig().withSeed(123L);

Transitioning to the Real Testbed

Once the simulator-based tests pass, switching to the GoGlobal sandbox is straightforward:

  1. Update config/goglobal-client.xml with real credentials:

    <property name="goglobal.agency" value="REAL_AGENCY_ID"/>
    <property name="goglobal.user" value="REAL_USERNAME"/>
    <property name="goglobal.password" value="REAL_PASSWORD"/>
    <property name="goglobal.apiServer" value="https://sandbox.apiservice.goglobal.travel/"/>
    

  2. Remove the simulator override in the E2E test @BeforeAll (or create a separate test class for live integration).

  3. 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