mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-09-12 19:58:21 +02:00
SaaS fixes (#6578)
Co-authored-by: Claude Opus 4.8 <[email protected]> Co-authored-by: James Brunton <[email protected]> Co-authored-by: Reece Browne <[email protected]> Co-authored-by: ConnorYoh <[email protected]> Co-authored-by: Reece <[email protected]> Co-authored-by: EthanHealy01 <[email protected]> Co-authored-by: Ludy <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
James Brunton
Reece Browne
ConnorYoh
Reece
EthanHealy01
Ludy
parent
96accea984
commit
ddf78d11ae
@@ -1,105 +0,0 @@
|
||||
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"
|
||||
Reference in New Issue
Block a user