Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -729,6 +729,8 @@ jobs:
image: $DEPOT_IMAGE_URL
sink-worker:
image: $DEPOT_IMAGE_URL
billing-worker:
image: $DEPOT_IMAGE_URL
EOF

cat e2e/docker-compose.override.yaml
Expand All @@ -749,6 +751,8 @@ jobs:
build: ..
sink-worker:
build: ..
billing-worker:
build: ..
EOF

cat e2e/docker-compose.override.yaml
Expand Down Expand Up @@ -794,15 +798,35 @@ jobs:

- name: Wait for worker to become ready
run: |
curl --retry 10 --retry-max-time 120 --retry-all-errors http://localhost:30000/healthz
curl --fail --retry 10 --retry-max-time 120 --retry-all-errors http://localhost:30000/healthz
curl --fail --retry 10 --retry-max-time 120 --retry-all-errors http://localhost:30001/healthz
docker ps
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- name: Run tests
- name: Run base tests
env:
OPENMETER_ADDRESS: http://localhost:38888
TZ: UTC
run: |
nix develop --impure .#ci -c make -C e2e test-base

- name: Restart Docker Compose infra for credits-disabled tests
run: |
docker compose -f docker-compose.infra.yaml -f docker-compose.openmeter.yaml -f docker-compose.override.yaml down -v
docker compose -f docker-compose.infra.yaml -f docker-compose.openmeter.yaml -f docker-compose.override.yaml -f docker-compose.credits-disabled.yaml up -d
working-directory: e2e

- name: Wait for worker to become ready with credits disabled
run: |
curl --fail --retry 10 --retry-max-time 120 --retry-all-errors http://localhost:30000/healthz
curl --fail --retry 10 --retry-max-time 120 --retry-all-errors http://localhost:30001/healthz
docker ps

- name: Run credits-disabled tests
env:
OPENMETER_ADDRESS: http://localhost:38888
TZ: UTC
run: |
nix develop --impure .#ci -c go test -v -count=1 -timeout 3m ./e2e/
nix develop --impure .#ci -c make -C e2e test-credits-disabled

- name: Cleanup Docker Compose
run: docker compose -f docker-compose.infra.yaml -f docker-compose.openmeter.yaml -f docker-compose.override.yaml down -v
Expand Down
39 changes: 34 additions & 5 deletions e2e/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ OPENMETER_ADDRESS ?= http://localhost:38888
BENCHTIME ?= 20x
# Number of times to repeat the whole benchmark (feed >1 into benchstat for variance).
COUNT ?= 1
OPENMETER_E2E_EXTRA_COMPOSE_FILES ?=

.PHONY: bench-governance
bench-governance: ## Benchmark governance query latency (1x1 baseline + 10/50/100 diagonal)
Expand All @@ -23,16 +24,43 @@ bench-governance-matrix: ## Benchmark governance query latency (full 3x3 custome

.PHONY: test-local
test-local: ## Run tests against local openmeter
$(call print-target)
$(MAKE) test-local-base
$(MAKE) test-local-credits-disabled

.PHONY: test-local-credits-disabled
test-local-credits-disabled: ## Run credits-disabled e2e tests against local openmeter
$(call print-target)
$(MAKE) env-local-down
$(MAKE) env-local-up
set -e; trap '$(MAKE) env-local-down' EXIT; \
OPENMETER_E2E_EXTRA_COMPOSE_FILES="-f docker-compose.credits-disabled.yaml" $(MAKE) env-local-up; \
$(MAKE) wait-local; \
$(MAKE) test-credits-disabled

# wait for sink-worker to be ready
curl --retry 10 --retry-max-time 120 --retry-all-errors http://localhost:30000/healthz
.PHONY: test-local-base
test-local-base: ## Run default e2e tests against local openmeter
$(call print-target)
$(MAKE) env-local-down
set -e; trap '$(MAKE) env-local-down' EXIT; \
$(MAKE) env-local-up; \
$(MAKE) wait-local; \
$(MAKE) test-base

TZ=UTC OPENMETER_ADDRESS=http://localhost:38888 go test -count=1 -v ./...
.PHONY: test-credits-disabled
test-credits-disabled: ## Run e2e tests that require credits.enabled=false
$(call print-target)
TZ=UTC OPENMETER_ADDRESS=$(OPENMETER_ADDRESS) go test -count=1 -v ./creditsdisabled

$(MAKE) env-local-down
.PHONY: test-base
test-base: ## Run the default e2e test package
$(call print-target)
TZ=UTC OPENMETER_ADDRESS=$(OPENMETER_ADDRESS) go test -count=1 -v ./

.PHONY: wait-local
wait-local:
$(call print-target)
curl --fail --retry 10 --retry-max-time 120 --retry-all-errors http://localhost:30000/healthz
curl --fail --retry 10 --retry-max-time 120 --retry-all-errors http://localhost:30001/healthz

.PHONY: env-local-down
env-local-down:
Expand All @@ -52,6 +80,7 @@ env-local-up:
-f docker-compose.debug-ports.yaml \
-f docker-compose.openmeter.yaml \
-f docker-compose.openmeter-local.yaml \
$(OPENMETER_E2E_EXTRA_COMPOSE_FILES) \
up -d --build --force-recreate

.PHONY: help
Expand Down
43 changes: 26 additions & 17 deletions e2e/billinginvoices_v3_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package e2e

import (
"net/http"
"slices"
"testing"
"time"

Expand All @@ -20,7 +21,7 @@ import (
// - Create a meter and feature for usage-based billing (v3)
// - Create and publish a plan with a unit rate card backed by the feature (v3)
// - Create a subscription for the customer (v3)
// - Advance the gathering invoice via the v1 InvoicePendingLinesAction to create a standard invoice
// - Create a charge-backed pending line and wait for charges to advance it into a standard invoice
// - List the customer's invoices via the v1 SDK; separate standard from gathering
// - GET the standard invoice via the v3 endpoint and assert the response shape
// - GET the gathering invoice via the v3 endpoint → 404 (gathering invoices are not exposed)
Expand Down Expand Up @@ -135,15 +136,16 @@ func TestV3GetBillingInvoice(t *testing.T) {
PaymentTerm: lo.ToPtr(api.PricePaymentTermInAdvance),
}))

invoiceAt := now.Add(time.Hour)
lineResp, err := v1.CreatePendingInvoiceLineWithResponse(t.Context(), customerID, api.InvoicePendingLineCreateInput{
Currency: "USD",
Lines: []api.InvoicePendingLineCreate{
{
Name: uniqueKey("inv_gathering_line_name"),
InvoiceAt: now.Add(-10 * time.Hour),
InvoiceAt: invoiceAt,
Period: api.Period{
From: now.Add(-24 * time.Hour),
To: now.Add(time.Hour),
To: invoiceAt,
},
Price: &price,
},
Expand Down Expand Up @@ -176,7 +178,7 @@ func TestV3GetBillingInvoice(t *testing.T) {
InvoiceAt: now.Add(-10 * time.Hour),
Period: api.Period{
From: now.Add(-24 * time.Hour),
To: now.Add(time.Hour),
To: now.Add(-2 * time.Hour),
},
Price: &price,
},
Expand All @@ -186,17 +188,24 @@ func TestV3GetBillingInvoice(t *testing.T) {
require.Equal(t, http.StatusCreated, lineResp.StatusCode(), "line: %s", string(lineResp.Body))
require.NotNil(t, lineResp.JSON201)

resp, err := v1.InvoicePendingLinesActionWithResponse(t.Context(), api.InvoicePendingLinesActionInput{
CustomerId: customerID,
ProgressiveBillingOverride: lo.ToPtr(true),
AsOf: lo.ToPtr(now.Add(-1 * time.Hour)),
})
require.NoError(t, err)
require.Equal(t, http.StatusCreated, resp.StatusCode(), "advance: %s", string(resp.Body))
require.NotNil(t, resp.JSON201)
require.NotEmpty(t, *resp.JSON201, "expected at least one standard invoice to be created")

invoiceID = (*resp.JSON201)[0].Id
ctx := t.Context()
customers := api.InvoiceListParamsCustomers{customerID}
assert.EventuallyWithT(t, func(c *assert.CollectT) {
listResp, err := v1.ListInvoicesWithResponse(ctx, &api.ListInvoicesParams{
Customers: &customers,
PageSize: lo.ToPtr(api.PaginationPageSize(100)),
})
require.NoError(c, err)
require.Equal(c, http.StatusOK, listResp.StatusCode(), "list invoices: %s", string(listResp.Body))
require.NotNil(c, listResp.JSON200)

standardInvoiceIdx := slices.IndexFunc(listResp.JSON200.Items, func(inv api.Invoice) bool {
return inv.Status != api.InvoiceStatusGathering
})
require.NotEqual(c, -1, standardInvoiceIdx, "expected charges to advance a pending line into a standard invoice")

invoiceID = listResp.JSON200.Items[standardInvoiceIdx].Id
}, time.Minute, time.Second)
require.NotEmpty(t, invoiceID)
})

Expand All @@ -212,10 +221,10 @@ func TestV3GetBillingInvoice(t *testing.T) {
require.NotNil(t, listResp.JSON200)
require.NotEmpty(t, listResp.JSON200.Items, "expected at least one invoice for customer %s (key: %s)", customerID, customerKey)

_, foundStandard := lo.Find(listResp.JSON200.Items, func(inv api.Invoice) bool {
standardInvoiceIdx := slices.IndexFunc(listResp.JSON200.Items, func(inv api.Invoice) bool {
return inv.Status != api.InvoiceStatusGathering
})
require.True(t, foundStandard, "expected at least one non-gathering invoice in the list")
require.NotEqual(t, -1, standardInvoiceIdx, "expected at least one non-gathering invoice in the list")
})

t.Run("Should return the invoice via v3 GET", func(t *testing.T) {
Expand Down
10 changes: 5 additions & 5 deletions e2e/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ postgres:
url: postgres://postgres:postgres@postgres:5432/postgres?sslmode=disable
autoMigrate: migration

# Credits configuration - disabled for e2e tests.
# Test cases in productcatalog_test.go (TestSettlementMode) depend on this setting
# to verify that credit_only settlement mode is rejected when credits are disabled.
# Credits are enabled for the default e2e suite so billing flows run in the same
# mode as production-like credit deployments. Credits-disabled compatibility
# tests run from e2e/creditsdisabled with CREDITS_* env overrides.
credits:
enabled: false
enableCreditThenInvoice: false
enabled: true
enableCreditThenInvoice: true

notification:
lock:
Expand Down
10 changes: 10 additions & 0 deletions e2e/creditsdisabled/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Credits Disabled E2E Tests

This package contains temporary pre-credit to credit upgrade compatibility tests.

The default e2e suite runs with credits enabled. Tests in this package exercise
behavior that must remain valid for installations that started before credits
were enabled, or that still run with credits disabled during the upgrade window.

Remove this package once credits are fully ready and credits-disabled deployments
are no longer supported by the e2e contract.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package e2e
package creditsdisabled

import (
"bytes"
Expand All @@ -21,8 +21,8 @@ func TestLedgerBackfillAccountsJob(t *testing.T) {
pool := initE2EPostgresPool(t)

suffix := strings.ToLower(ulid.Make().String())
customerA := CreateCustomerWithSubject(t, client, "ledger-backfill-"+suffix+"-a", "ledger-backfill-subject-"+suffix+"-a")
customerB := CreateCustomerWithSubject(t, client, "ledger-backfill-"+suffix+"-b", "ledger-backfill-subject-"+suffix+"-b")
customerA := createCustomerWithSubject(t, client, "ledger-backfill-"+suffix+"-a", "ledger-backfill-subject-"+suffix+"-a")
customerB := createCustomerWithSubject(t, client, "ledger-backfill-"+suffix+"-b", "ledger-backfill-subject-"+suffix+"-b")

customerIDs := []string{customerA.Id, customerB.Id}
namespace := getCustomerNamespace(t, pool, customerA.Id)
Expand Down Expand Up @@ -112,7 +112,7 @@ func runJobsBackfillAccounts(t *testing.T, args ...string) string {
base = append(base, args...)

cmd := exec.CommandContext(t.Context(), "docker", base...)
cmd.Dir = "."
cmd.Dir = ".."

var out bytes.Buffer
cmd.Stdout = &out
Expand Down
Loading
Loading