Spaces:
Sleeping
Sleeping
| # Hotel Search App β QA Report | |
| ## Scope of Testing | |
| This QA report validates the Hotel Search App against the Architect's plan (`plan.md`) and the Builder's summary (`build.md`). Testing covers schema adherence, input validation, edge cases, adversarial inputs, security, and interface contract enforcement. | |
| --- | |
| ## 1. Plan vs. Build Compliance | |
| | Plan Requirement | Implemented? | Notes | | |
| |------------------|:------------:|-------| | |
| | Free-form natural language input | Yes | Single Gradio Textbox, 5-line height | | |
| | SerpApi `google_hotels` engine | Yes | Correct engine parameter in API call | | |
| | Date extraction (5+ formats) | Yes | MM/DD/YYYY, YYYY-MM-DD, Month Day Year, Month D-D Year, single dates | | |
| | Price range extraction | Yes | Range, upper-bound, lower-bound patterns | | |
| | Travel-agency link exclusion | Yes | 20+ agency domains in blocklist | | |
| | Top 15 results | Yes | `properties[:15]` enforced | | |
| | Direct hotel website links | Yes | `get_hotel_link` with fallback to Google search | | |
| | Hugging Face deployment ready | Yes | Single `app.py`, `requirements.txt`, README metadata | | |
| | API key via env variable or UI | Yes | Checks UI field first, then `SERPAPI_KEY` / `SERPAPI_API_KEY` | | |
| | Graceful error handling | Yes | Try/except wraps full pipeline; friendly HTML messages | | |
| **Verdict**: All plan elements are implemented. No missing features. | |
| --- | |
| ## 2. Input Validation Tests | |
| ### 2.1 Empty Input | |
| | Test | Input | Expected | Result | | |
| |------|-------|----------|--------| | |
| | Blank string | `""` | Error prompt | PASS β "Please enter a hotel description to search." | | |
| | Whitespace only | `" "` | Error prompt | PASS β Same message | | |
| ### 2.2 Missing API Key | |
| | Test | Input | Key | Expected | Result | | |
| |------|-------|-----|----------|--------| | |
| | No key anywhere | Any text | `""` (no env var) | Key prompt | PASS β "SerpApi key required" | | |
| ### 2.3 Adversarial / Gibberish Input | |
| | Test | Input | Expected | Result | | |
| |------|-------|----------|--------| | |
| | Random characters | `"asdfghjkl 12345"` | No crash; search attempt or no-results | PASS β Falls back to raw text as query | | |
| | SQL injection attempt | `"'; DROP TABLE hotels; --"` | No crash; input treated as text | PASS β Regex simply won't match; text sent to SerpApi as query | | |
| | XSS attempt | `"<script>alert('xss')</script>"` | No script execution | PASS β Gradio HTML component sanitizes output | | |
| | Extremely long input | 5000-character string | No crash | PASS β First 120 chars used as query if no location found | | |
| --- | |
| ## 3. Date Parsing Edge Cases | |
| | Input Fragment | Extracted Check-in | Extracted Check-out | Result | | |
| |----------------|--------------------|--------------------|--------| | |
| | `"March 15 to March 18, 2026"` | 2026-03-15 | 2026-03-18 | PASS | | |
| | `"3/15/2026 - 3/18/2026"` | 2026-03-15 | 2026-03-18 | PASS | | |
| | `"2026-03-15 to 2026-03-18"` | 2026-03-15 | 2026-03-18 | PASS | | |
| | `"March 15, 2026"` (single date) | 2026-03-15 | 2026-03-16 | PASS β defaults to 1 night | | |
| | No dates provided | Tomorrow | Tomorrow + 1 | PASS β sensible defaults | | |
| | Past date `"Jan 1, 2020"` | Adjusted to tomorrow | Tomorrow + 1 | PASS β past-date guard works | | |
| | `"March 15-18, 2026"` (range) | 2026-03-15 | 2026-03-18 | PASS β compact range regex | | |
| --- | |
| ## 4. Price Parsing Edge Cases | |
| | Input Fragment | min_price | max_price | Result | | |
| |----------------|-----------|-----------|--------| | |
| | `"$100 to $200"` | 100 | 200 | PASS | | |
| | `"under $200"` | None | 200 | PASS | | |
| | `"budget of $150"` | None | 150 | PASS | | |
| | `"above $100"` | 100 | None | PASS | | |
| | `"at least $50"` | 50 | None | PASS | | |
| | No price mentioned | None | None | PASS β no price filter applied | | |
| --- | |
| ## 5. Location Extraction Tests | |
| | Input Fragment | Extracted Location | Result | | |
| |----------------|-------------------|--------| | |
| | `"hotel in Austin, TX"` | Austin, TX | PASS | | |
| | `"near Times Square"` | Times Square | PASS | | |
| | `"hotels in San Francisco"` | San Francisco | PASS | | |
| | `"close to downtown Nashville"` | downtown Nashville | PASS | | |
| | No location keyword | Falls back to first sentence | PASS β graceful fallback | | |
| --- | |
| ## 6. Travel-Agency Link Filtering | |
| Verified that the blocklist covers: Expedia, Booking.com, Hotels.com, Trivago, Kayak, Priceline, Orbitz, Travelocity, Agoda, Trip.com, Hotwire, CheapTickets, LastMinute, eDreams, Opodo, Wotif, Zuji, MakeMyTrip, Goibibo, Yatra. | |
| - If all results happen to be travel-agency links, the filter relaxes (threshold < 3) to avoid showing zero results. This is a pragmatic trade-off documented in `build.md`. | |
| --- | |
| ## 7. Security Review | |
| | Check | Status | Notes | | |
| |-------|--------|-------| | |
| | No hard-coded API keys | PASS | Key from UI (masked) or env var only | | |
| | No `eval()` or `exec()` on user input | PASS | Input only used in regex and as SerpApi query param | | |
| | No shell command execution | PASS | No `subprocess` or `os.system` calls | | |
| | External links use `rel='noopener noreferrer'` | PASS | Prevents reverse tabnapping | | |
| | Stack traces hidden from user | PASS | Generic error message with exception text only | | |
| | Gradio `type="password"` for API key | PASS | Input is masked in UI | | |
| --- | |
| ## 8. Interface Contract Verification | |
| - Parser output dict matches the JSON schema defined in `plan.md`. | |
| - SerpApi response is consumed via `results.get("properties", [])` with safe `.get()` access throughout. | |
| - HTML output is well-formed; tested with missing fields (no image, no rating, no amenities). | |
| --- | |
| ## 9. Findings & Recommendations | |
| ### Issues Found (non-blocking) | |
| 1. **Relative date phrases** like "next weekend" or "this Friday" are not parsed. The app defaults to tomorrow, which is acceptable but could be improved. | |
| 2. **Feature matching against results** is not performed post-search. The required/desired/avoid features are extracted but only influence the search query indirectly. SerpApi handles the location and price filtering, but amenity-based re-ranking is not implemented. | |
| 3. **No automated test suite**. Manual tests pass but a `pytest` suite would strengthen CI/CD. | |
| ### Recommendations | |
| - Add a lightweight NLP layer (or LLM call) for better feature classification and relative date parsing in a future version. | |
| - Add unit tests for `parse_user_input` covering the edge cases above. | |
| - Consider rate-limiting or caching SerpApi calls to avoid quota exhaustion during heavy use. | |