mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 03:20:46 +02:00
106 lines
6.3 KiB
Gherkin
106 lines
6.3 KiB
Gherkin
Feature: PAYG shadow-mode charging
|
|
# End-to-end coverage for the PAYG shadow charging engine via the filter +
|
|
# interceptor stack landed in PR #6519. Runs against the saas-profile
|
|
# docker-compose target (testing/compose/docker-compose-saas.yml).
|
|
#
|
|
# Each scenario sets up a test team in PAYG_SHADOW mode, hits a real tool
|
|
# endpoint, then asserts the shape of the rows written to payg_shadow_charge
|
|
# and processing_job / processing_job_step.
|
|
#
|
|
# Lives under features/payg so it's only loaded when the saas-cucumber job
|
|
# explicitly includes this directory (separate from the main behave run
|
|
# which boots the proprietary-flavor stack and has no PAYG tables).
|
|
|
|
Background:
|
|
Given the SaaS stack is running with PAYG enabled
|
|
And team "payg-cucumber-team" exists with wallet_policy.engine = "PAYG_SHADOW"
|
|
And I am authenticated as a member of team "payg-cucumber-team"
|
|
|
|
Scenario: First tool call writes a CHARGED shadow row
|
|
Given there are no existing shadow charges for team "payg-cucumber-team"
|
|
When I POST a single-page PDF to "/api/v1/security/add-password"
|
|
Then the response status is 200
|
|
And exactly 1 shadow charge row exists for team "payg-cucumber-team"
|
|
And the latest shadow charge row has status "CHARGED"
|
|
And the latest shadow charge row has payg_units >= 1
|
|
And the latest shadow charge row's job is OPEN
|
|
And the latest job has 1 step recorded with status "OK"
|
|
|
|
Scenario: Lineage join — second call on the same output joins the first process
|
|
Given there are no existing shadow charges for team "payg-cucumber-team"
|
|
When I POST a single-page PDF to "/api/v1/security/add-password"
|
|
And I take the response body as "step1-output"
|
|
And I POST "step1-output" to "/api/v1/security/sanitize-pdf"
|
|
Then exactly 1 shadow charge row exists for team "payg-cucumber-team"
|
|
# The second call joined the first process — no new shadow row
|
|
And the latest job has step_count = 2
|
|
And the latest job is OPEN
|
|
|
|
Scenario: First-step 5xx refunds the shadow row + closes the process
|
|
# No reliably-5xx-ing real tool endpoint exists (every malformed input is
|
|
# caught as 4xx by GlobalExceptionHandler), so we drive the refund path
|
|
# through PaygCucumberThrowController — a @Profile("payg-cucumber") stub
|
|
# that always throws IllegalStateException → 500. The profile is active
|
|
# only inside docker-compose-saas.yml, never in production.
|
|
Given there are no existing shadow charges for team "payg-cucumber-team"
|
|
When I POST a single-page PDF to "/api/v1/payg-cucumber/throw-500"
|
|
Then the response status is 500
|
|
And exactly 1 shadow charge row exists for team "payg-cucumber-team"
|
|
And the latest shadow charge row has status "REFUNDED"
|
|
And the latest shadow charge row's refunded_at is not null
|
|
And the latest shadow charge row's refund_reason starts with "first-step-5xx"
|
|
And the latest job is CLOSED
|
|
And the latest job has 1 step recorded with status "FAILED"
|
|
|
|
# ──────────────────────────────────────────────────────────────────────────
|
|
# NOTE: The kill-switch scenario (PAYG_FILTER_ENABLED=false) is NOT
|
|
# automated — it requires restarting the stirling-pdf-saas container with
|
|
# a different env var mid-run, which couples test setup to compose state
|
|
# more than it's worth for a flag that only flips during incident response.
|
|
# See notes/PAYG_DESIGN.md §7.5.2 "PAYG cucumber: manual-only scenarios".
|
|
# ──────────────────────────────────────────────────────────────────────────
|
|
|
|
Scenario: 4xx leaves the shadow row CHARGED (customer pays for the attempt)
|
|
# /sanitize-pdf on an encrypted PDF without the password reliably 400s
|
|
# via GlobalExceptionHandler's PdfPasswordException → ProblemDetail path.
|
|
# We chain: first call encrypts a PDF (CHARGED), second call tries to
|
|
# sanitize WITHOUT the password and 400s. The 4xx assertion is on the
|
|
# SECOND call's behaviour.
|
|
Given there are no existing shadow charges for team "payg-cucumber-team"
|
|
When I POST a single-page PDF to "/api/v1/security/add-password"
|
|
And I take the response body as "encrypted"
|
|
And I POST "encrypted" to "/api/v1/security/sanitize-pdf"
|
|
Then the response status is >= 400 and < 500
|
|
# Note: shadow_charges count is 1 because the second call lineage-joins
|
|
# the first (its input matches the first's output). The 4xx therefore
|
|
# appears as a FAILED step on the existing process, not a new shadow row.
|
|
And exactly 1 shadow charge row exists for team "payg-cucumber-team"
|
|
And the latest shadow charge row has status "CHARGED"
|
|
And the latest job has step_count = 2
|
|
And the latest step's error_code matches the response status
|
|
|
|
Scenario: ZIP-returning tool records OUTPUT signatures per inner PDF
|
|
# /split-pages with multiple page numbers returns a ZIP. Stirling sends
|
|
# application/octet-stream rather than application/zip — the extractor
|
|
# sniffs the PK\x03\x04 magic so the ZIP unpack path still fires.
|
|
Given there are no existing shadow charges for team "payg-cucumber-team"
|
|
When I POST a 3-page PDF to "/api/v1/general/split-pages" with form fields:
|
|
| pageNumbers | 1,2 |
|
|
Then the response status is 200
|
|
And exactly 1 shadow charge row exists for team "payg-cucumber-team"
|
|
And the latest job has at least 2 OUTPUT artifact hashes recorded
|
|
|
|
Scenario: Multi-file input writes a single shadow row sized by the group
|
|
Given there are no existing shadow charges for team "payg-cucumber-team"
|
|
When I POST two single-page PDFs as a multi-file payload to "/api/v1/general/merge-pdfs"
|
|
Then the response status is 200
|
|
And exactly 1 shadow charge row exists for team "payg-cucumber-team"
|
|
And the latest shadow charge row has payg_units >= 1
|
|
|
|
Scenario: PIPELINE header sets the job source
|
|
Given there are no existing shadow charges for team "payg-cucumber-team"
|
|
When I POST a single-page PDF with header "X-Stirling-Automation: true" to "/api/v1/security/add-password"
|
|
Then the response status is 200
|
|
And exactly 1 shadow charge row exists for team "payg-cucumber-team"
|
|
And the latest job's source is "PIPELINE"
|