From 98f87fb291aaffa891e030f57d0e3dc4f8a10e45 Mon Sep 17 00:00:00 2001 From: aman Date: Fri, 25 Sep 2026 14:10:43 +0530 Subject: [PATCH 1/2] feat(store): org invoices search skips soft-deleted rows Both the listing and the group-by count read invoices and billing customers through the live-row helpers, and join organizations so a soft-deleted org reports nothing. --- .../store/postgres/org_invoices_repository.go | 32 ++++++++++++++----- .../postgres/org_invoices_repository_test.go | 18 +++++------ 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/internal/store/postgres/org_invoices_repository.go b/internal/store/postgres/org_invoices_repository.go index 4754a9d59f..f791fc5053 100644 --- a/internal/store/postgres/org_invoices_repository.go +++ b/internal/store/postgres/org_invoices_repository.go @@ -162,7 +162,7 @@ func (r OrgInvoicesRepository) prepareDataQuery(orgID string, rql *rql.Query) (s } func (r OrgInvoicesRepository) prepareGroupByQuery(orgID string, rql *rql.Query) (string, []any, error) { - query := dialect.From(TABLE_BILLING_INVOICES).Prepared(true). + query := fromLive(TABLE_BILLING_INVOICES).Prepared(true). Select( goqu.COUNT("*").As("count"), goqu.I(TABLE_BILLING_INVOICES+"."+COLUMN_STATE).As("values"), @@ -171,9 +171,17 @@ func (r OrgInvoicesRepository) prepareGroupByQuery(orgID string, rql *rql.Query) goqu.T(TABLE_BILLING_CUSTOMERS), goqu.On(goqu.I(TABLE_BILLING_INVOICES+".customer_id").Eq(goqu.I(TABLE_BILLING_CUSTOMERS+".id"))), ). - Where(goqu.Ex{ - TABLE_BILLING_CUSTOMERS + "." + COLUMN_ORG_ID: orgID, - }) + InnerJoin( + goqu.T(TABLE_ORGANIZATIONS), + goqu.On(goqu.I(TABLE_BILLING_CUSTOMERS+"."+COLUMN_ORG_ID).Eq(goqu.I(TABLE_ORGANIZATIONS+"."+COLUMN_ID))), + ). + Where( + goqu.Ex{ + TABLE_BILLING_CUSTOMERS + "." + COLUMN_ORG_ID: orgID, + }, + live(TABLE_BILLING_CUSTOMERS), + live(TABLE_ORGANIZATIONS), + ) // Apply the same filters as the main query for _, filter := range rql.Filters { @@ -192,7 +200,7 @@ func (r OrgInvoicesRepository) prepareGroupByQuery(orgID string, rql *rql.Query) } func (r OrgInvoicesRepository) buildBaseQuery(orgID string) *goqu.SelectDataset { - return dialect.From(TABLE_BILLING_INVOICES).Prepared(true). + return fromLive(TABLE_BILLING_INVOICES).Prepared(true). Select( goqu.I(TABLE_BILLING_INVOICES+"."+COLUMN_ID).As("invoice_id"), goqu.I(TABLE_BILLING_INVOICES+"."+COLUMN_AMOUNT).As("invoice_amount"), @@ -206,9 +214,17 @@ func (r OrgInvoicesRepository) buildBaseQuery(orgID string) *goqu.SelectDataset goqu.T(TABLE_BILLING_CUSTOMERS), goqu.On(goqu.I(TABLE_BILLING_INVOICES+".customer_id").Eq(goqu.I(TABLE_BILLING_CUSTOMERS+".id"))), ). - Where(goqu.Ex{ - TABLE_BILLING_CUSTOMERS + "." + COLUMN_ORG_ID: orgID, - }) + InnerJoin( + goqu.T(TABLE_ORGANIZATIONS), + goqu.On(goqu.I(TABLE_BILLING_CUSTOMERS+"."+COLUMN_ORG_ID).Eq(goqu.I(TABLE_ORGANIZATIONS+"."+COLUMN_ID))), + ). + Where( + goqu.Ex{ + TABLE_BILLING_CUSTOMERS + "." + COLUMN_ORG_ID: orgID, + }, + live(TABLE_BILLING_CUSTOMERS), + live(TABLE_ORGANIZATIONS), + ) } func (r OrgInvoicesRepository) addFilter(query *goqu.SelectDataset, filter rql.Filter) *goqu.SelectDataset { diff --git a/internal/store/postgres/org_invoices_repository_test.go b/internal/store/postgres/org_invoices_repository_test.go index bcfa40d207..cc9949f5c7 100644 --- a/internal/store/postgres/org_invoices_repository_test.go +++ b/internal/store/postgres/org_invoices_repository_test.go @@ -23,7 +23,7 @@ func TestOrgInvoicesRepository_prepareDataQuery(t *testing.T) { Limit: 10, Offset: 20, }, - wantSQL: `SELECT "billing_invoices"."id" AS "invoice_id", "billing_invoices"."amount" AS "invoice_amount", "billing_invoices"."currency" AS "invoice_currency", "billing_invoices"."state" AS "invoice_state", "billing_invoices"."hosted_url" AS "invoice_hosted_url", "billing_invoices"."created_at" AS "invoice_created_at", "billing_customers"."org_id" AS "org_id" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") WHERE ("billing_customers"."org_id" = $1) LIMIT $2 OFFSET $3`, + wantSQL: `SELECT "billing_invoices"."id" AS "invoice_id", "billing_invoices"."amount" AS "invoice_amount", "billing_invoices"."currency" AS "invoice_currency", "billing_invoices"."state" AS "invoice_state", "billing_invoices"."hosted_url" AS "invoice_hosted_url", "billing_invoices"."created_at" AS "invoice_created_at", "billing_customers"."org_id" AS "org_id" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") INNER JOIN "organizations" ON ("billing_customers"."org_id" = "organizations"."id") WHERE (("billing_invoices"."deleted_at" IS NULL) AND ("billing_customers"."org_id" = $1) AND ("billing_customers"."deleted_at" IS NULL) AND ("organizations"."deleted_at" IS NULL)) LIMIT $2 OFFSET $3`, wantParams: []any{"org123", int64(10), int64(20)}, wantErr: false, }, @@ -41,7 +41,7 @@ func TestOrgInvoicesRepository_prepareDataQuery(t *testing.T) { Limit: 10, Offset: 50, }, - wantSQL: `SELECT "billing_invoices"."id" AS "invoice_id", "billing_invoices"."amount" AS "invoice_amount", "billing_invoices"."currency" AS "invoice_currency", "billing_invoices"."state" AS "invoice_state", "billing_invoices"."hosted_url" AS "invoice_hosted_url", "billing_invoices"."created_at" AS "invoice_created_at", "billing_customers"."org_id" AS "org_id" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") WHERE (("billing_customers"."org_id" = $1) AND ("billing_invoices"."amount" >= $2)) LIMIT $3 OFFSET $4`, + wantSQL: `SELECT "billing_invoices"."id" AS "invoice_id", "billing_invoices"."amount" AS "invoice_amount", "billing_invoices"."currency" AS "invoice_currency", "billing_invoices"."state" AS "invoice_state", "billing_invoices"."hosted_url" AS "invoice_hosted_url", "billing_invoices"."created_at" AS "invoice_created_at", "billing_customers"."org_id" AS "org_id" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") INNER JOIN "organizations" ON ("billing_customers"."org_id" = "organizations"."id") WHERE (("billing_invoices"."deleted_at" IS NULL) AND ("billing_customers"."org_id" = $1) AND ("billing_customers"."deleted_at" IS NULL) AND ("organizations"."deleted_at" IS NULL) AND ("billing_invoices"."amount" >= $2)) LIMIT $3 OFFSET $4`, wantParams: []any{"org123", int64(1000), int64(10), int64(50)}, wantErr: false, }, @@ -60,7 +60,7 @@ func TestOrgInvoicesRepository_prepareDataQuery(t *testing.T) { Limit: 10, Offset: 30, }, - wantSQL: `SELECT "billing_invoices"."id" AS "invoice_id", "billing_invoices"."amount" AS "invoice_amount", "billing_invoices"."currency" AS "invoice_currency", "billing_invoices"."state" AS "invoice_state", "billing_invoices"."hosted_url" AS "invoice_hosted_url", "billing_invoices"."created_at" AS "invoice_created_at", "billing_customers"."org_id" AS "org_id" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") WHERE (("billing_customers"."org_id" = $1) AND ("billing_invoices"."state" = $2) AND ((CAST("billing_invoices"."state" AS TEXT) ILIKE $3) OR (CAST("billing_invoices"."hosted_url" AS TEXT) ILIKE $4) OR (CAST("billing_invoices"."amount" AS TEXT) ILIKE $5))) LIMIT $6 OFFSET $7`, + wantSQL: `SELECT "billing_invoices"."id" AS "invoice_id", "billing_invoices"."amount" AS "invoice_amount", "billing_invoices"."currency" AS "invoice_currency", "billing_invoices"."state" AS "invoice_state", "billing_invoices"."hosted_url" AS "invoice_hosted_url", "billing_invoices"."created_at" AS "invoice_created_at", "billing_customers"."org_id" AS "org_id" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") INNER JOIN "organizations" ON ("billing_customers"."org_id" = "organizations"."id") WHERE (("billing_invoices"."deleted_at" IS NULL) AND ("billing_customers"."org_id" = $1) AND ("billing_customers"."deleted_at" IS NULL) AND ("organizations"."deleted_at" IS NULL) AND ("billing_invoices"."state" = $2) AND ((CAST("billing_invoices"."state" AS TEXT) ILIKE $3) OR (CAST("billing_invoices"."hosted_url" AS TEXT) ILIKE $4) OR (CAST("billing_invoices"."amount" AS TEXT) ILIKE $5))) LIMIT $6 OFFSET $7`, wantParams: []any{"org123", "paid", "%test%", "%test%", "%test%", int64(10), int64(30)}, wantErr: false, }, @@ -77,7 +77,7 @@ func TestOrgInvoicesRepository_prepareDataQuery(t *testing.T) { Limit: 10, Offset: 40, }, - wantSQL: `SELECT "billing_invoices"."id" AS "invoice_id", "billing_invoices"."amount" AS "invoice_amount", "billing_invoices"."currency" AS "invoice_currency", "billing_invoices"."state" AS "invoice_state", "billing_invoices"."hosted_url" AS "invoice_hosted_url", "billing_invoices"."created_at" AS "invoice_created_at", "billing_customers"."org_id" AS "org_id" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") WHERE ("billing_customers"."org_id" = $1) ORDER BY "invoice_state" DESC LIMIT $2 OFFSET $3`, + wantSQL: `SELECT "billing_invoices"."id" AS "invoice_id", "billing_invoices"."amount" AS "invoice_amount", "billing_invoices"."currency" AS "invoice_currency", "billing_invoices"."state" AS "invoice_state", "billing_invoices"."hosted_url" AS "invoice_hosted_url", "billing_invoices"."created_at" AS "invoice_created_at", "billing_customers"."org_id" AS "org_id" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") INNER JOIN "organizations" ON ("billing_customers"."org_id" = "organizations"."id") WHERE (("billing_invoices"."deleted_at" IS NULL) AND ("billing_customers"."org_id" = $1) AND ("billing_customers"."deleted_at" IS NULL) AND ("organizations"."deleted_at" IS NULL)) ORDER BY "invoice_state" DESC LIMIT $2 OFFSET $3`, wantParams: []any{"org123", int64(10), int64(40)}, wantErr: false, }, @@ -95,7 +95,7 @@ func TestOrgInvoicesRepository_prepareDataQuery(t *testing.T) { Limit: 10, Offset: 25, }, - wantSQL: `SELECT "billing_invoices"."id" AS "invoice_id", "billing_invoices"."amount" AS "invoice_amount", "billing_invoices"."currency" AS "invoice_currency", "billing_invoices"."state" AS "invoice_state", "billing_invoices"."hosted_url" AS "invoice_hosted_url", "billing_invoices"."created_at" AS "invoice_created_at", "billing_customers"."org_id" AS "org_id" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") WHERE ("billing_customers"."org_id" = $1) ORDER BY "invoice_state" ASC, "invoice_amount" DESC LIMIT $2 OFFSET $3`, + wantSQL: `SELECT "billing_invoices"."id" AS "invoice_id", "billing_invoices"."amount" AS "invoice_amount", "billing_invoices"."currency" AS "invoice_currency", "billing_invoices"."state" AS "invoice_state", "billing_invoices"."hosted_url" AS "invoice_hosted_url", "billing_invoices"."created_at" AS "invoice_created_at", "billing_customers"."org_id" AS "org_id" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") INNER JOIN "organizations" ON ("billing_customers"."org_id" = "organizations"."id") WHERE (("billing_invoices"."deleted_at" IS NULL) AND ("billing_customers"."org_id" = $1) AND ("billing_customers"."deleted_at" IS NULL) AND ("organizations"."deleted_at" IS NULL)) ORDER BY "invoice_state" ASC, "invoice_amount" DESC LIMIT $2 OFFSET $3`, wantParams: []any{"org123", int64(10), int64(25)}, wantErr: false, }, @@ -150,7 +150,7 @@ func TestOrgInvoicesRepository_prepareGroupByQuery(t *testing.T) { rql: &rql.Query{ GroupBy: []string{"state"}, }, - wantSQL: `SELECT COUNT(*) AS "count", "billing_invoices"."state" AS "values" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") WHERE ("billing_customers"."org_id" = $1) GROUP BY "billing_invoices"."state"`, + wantSQL: `SELECT COUNT(*) AS "count", "billing_invoices"."state" AS "values" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") INNER JOIN "organizations" ON ("billing_customers"."org_id" = "organizations"."id") WHERE (("billing_invoices"."deleted_at" IS NULL) AND ("billing_customers"."org_id" = $1) AND ("billing_customers"."deleted_at" IS NULL) AND ("organizations"."deleted_at" IS NULL)) GROUP BY "billing_invoices"."state"`, wantParams: []any{"org123"}, wantErr: false, }, @@ -167,7 +167,7 @@ func TestOrgInvoicesRepository_prepareGroupByQuery(t *testing.T) { }, }, }, - wantSQL: `SELECT COUNT(*) AS "count", "billing_invoices"."state" AS "values" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") WHERE (("billing_customers"."org_id" = $1) AND ("billing_invoices"."amount" >= $2)) GROUP BY "billing_invoices"."state"`, + wantSQL: `SELECT COUNT(*) AS "count", "billing_invoices"."state" AS "values" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") INNER JOIN "organizations" ON ("billing_customers"."org_id" = "organizations"."id") WHERE (("billing_invoices"."deleted_at" IS NULL) AND ("billing_customers"."org_id" = $1) AND ("billing_customers"."deleted_at" IS NULL) AND ("organizations"."deleted_at" IS NULL) AND ("billing_invoices"."amount" >= $2)) GROUP BY "billing_invoices"."state"`, wantParams: []any{"org123", int64(1000)}, wantErr: false, }, @@ -178,7 +178,7 @@ func TestOrgInvoicesRepository_prepareGroupByQuery(t *testing.T) { GroupBy: []string{"state"}, Search: "test", }, - wantSQL: `SELECT COUNT(*) AS "count", "billing_invoices"."state" AS "values" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") WHERE (("billing_customers"."org_id" = $1) AND ((CAST("billing_invoices"."state" AS TEXT) ILIKE $2) OR (CAST("billing_invoices"."hosted_url" AS TEXT) ILIKE $3) OR (CAST("billing_invoices"."amount" AS TEXT) ILIKE $4))) GROUP BY "billing_invoices"."state"`, + wantSQL: `SELECT COUNT(*) AS "count", "billing_invoices"."state" AS "values" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") INNER JOIN "organizations" ON ("billing_customers"."org_id" = "organizations"."id") WHERE (("billing_invoices"."deleted_at" IS NULL) AND ("billing_customers"."org_id" = $1) AND ("billing_customers"."deleted_at" IS NULL) AND ("organizations"."deleted_at" IS NULL) AND ((CAST("billing_invoices"."state" AS TEXT) ILIKE $2) OR (CAST("billing_invoices"."hosted_url" AS TEXT) ILIKE $3) OR (CAST("billing_invoices"."amount" AS TEXT) ILIKE $4))) GROUP BY "billing_invoices"."state"`, wantParams: []any{"org123", "%test%", "%test%", "%test%"}, wantErr: false, }, @@ -196,7 +196,7 @@ func TestOrgInvoicesRepository_prepareGroupByQuery(t *testing.T) { }, Search: "test", }, - wantSQL: `SELECT COUNT(*) AS "count", "billing_invoices"."state" AS "values" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") WHERE (("billing_customers"."org_id" = $1) AND ("billing_invoices"."amount" >= $2) AND ((CAST("billing_invoices"."state" AS TEXT) ILIKE $3) OR (CAST("billing_invoices"."hosted_url" AS TEXT) ILIKE $4) OR (CAST("billing_invoices"."amount" AS TEXT) ILIKE $5))) GROUP BY "billing_invoices"."state"`, + wantSQL: `SELECT COUNT(*) AS "count", "billing_invoices"."state" AS "values" FROM "billing_invoices" INNER JOIN "billing_customers" ON ("billing_invoices"."customer_id" = "billing_customers"."id") INNER JOIN "organizations" ON ("billing_customers"."org_id" = "organizations"."id") WHERE (("billing_invoices"."deleted_at" IS NULL) AND ("billing_customers"."org_id" = $1) AND ("billing_customers"."deleted_at" IS NULL) AND ("organizations"."deleted_at" IS NULL) AND ("billing_invoices"."amount" >= $2) AND ((CAST("billing_invoices"."state" AS TEXT) ILIKE $3) OR (CAST("billing_invoices"."hosted_url" AS TEXT) ILIKE $4) OR (CAST("billing_invoices"."amount" AS TEXT) ILIKE $5))) GROUP BY "billing_invoices"."state"`, wantParams: []any{"org123", int64(1000), "%test%", "%test%", "%test%"}, wantErr: false, }, From 06a753d5d20b7fbfcbb99fcb4921ccddf06053d0 Mon Sep 17 00:00:00 2001 From: aman Date: Fri, 25 Sep 2026 14:10:43 +0530 Subject: [PATCH 2/2] test(store): check the org invoices search against postgres with soft-deleted rows Seeds a soft-deleted invoice, one under a soft-deleted billing customer, and one under a soft-deleted organization, then checks the listing and the group counts. --- .../org_invoices_repository_pg_test.go | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 internal/store/postgres/org_invoices_repository_pg_test.go diff --git a/internal/store/postgres/org_invoices_repository_pg_test.go b/internal/store/postgres/org_invoices_repository_pg_test.go new file mode 100644 index 0000000000..13b4883bb8 --- /dev/null +++ b/internal/store/postgres/org_invoices_repository_pg_test.go @@ -0,0 +1,126 @@ +package postgres_test + +import ( + "context" + "fmt" + "io" + "log/slog" + "testing" + + "github.com/ory/dockertest" + "github.com/raystack/frontier/internal/store/postgres" + "github.com/raystack/frontier/pkg/db" + "github.com/raystack/salt/rql" + "github.com/stretchr/testify/suite" +) + +// Runs the search against a real postgres to check that soft-deleted invoices, +// billing customers, and organizations stay out of both the list and the counts. +type OrgInvoicesRepositoryPGTestSuite struct { + suite.Suite + ctx context.Context + client *db.Client + pool *dockertest.Pool + resource *dockertest.Resource + repository *postgres.OrgInvoicesRepository +} + +func (s *OrgInvoicesRepositoryPGTestSuite) SetupSuite() { + var err error + logger := slog.New(slog.NewTextHandler(io.Discard, nil)) + s.client, s.pool, s.resource, err = newTestClient(logger) + if err != nil { + s.T().Fatal(err) + } + s.ctx = context.TODO() + s.repository = postgres.NewOrgInvoicesRepository(s.client) +} + +func (s *OrgInvoicesRepositoryPGTestSuite) TearDownSuite() { + if err := purgeDocker(s.pool, s.resource); err != nil { + s.T().Fatal(err) + } +} + +func (s *OrgInvoicesRepositoryPGTestSuite) SetupTest() { + s.exec(`INSERT INTO organizations (name, title) VALUES ('oi-live', 'Live Org'), ('oi-gone', 'Gone Org')`) + + s.customer("oi-cust-live", "oi-live") + s.customer("oi-cust-gone", "oi-live") + s.customer("oi-cust-other", "oi-gone") + + s.invoice("oi-cust-live", "oi-inv-live", "paid") + s.invoice("oi-cust-live", "oi-inv-deleted", "paid") + s.invoice("oi-cust-gone", "oi-inv-orphan", "open") + s.invoice("oi-cust-other", "oi-inv-otherorg", "paid") + + s.exec(`UPDATE billing_invoices SET deleted_at = now() WHERE hosted_url = 'oi-inv-deleted'`) + s.exec(`UPDATE billing_customers SET deleted_at = now() WHERE name = 'oi-cust-gone'`) + s.exec(`UPDATE organizations SET deleted_at = now() WHERE name = 'oi-gone'`) +} + +func (s *OrgInvoicesRepositoryPGTestSuite) TearDownTest() { + queries := []string{} + for _, table := range []string{postgres.TABLE_BILLING_INVOICES, postgres.TABLE_BILLING_CUSTOMERS, + postgres.TABLE_ORGANIZATIONS} { + queries = append(queries, fmt.Sprintf("TRUNCATE TABLE %s RESTART IDENTITY CASCADE", table)) + } + if err := execQueries(s.ctx, s.client, queries); err != nil { + s.T().Fatal(err) + } +} + +func (s *OrgInvoicesRepositoryPGTestSuite) exec(query string, args ...any) { + s.T().Helper() + execSQL(s.T(), s.ctx, s.client, query, args...) +} + +func (s *OrgInvoicesRepositoryPGTestSuite) orgID(name string) string { + s.T().Helper() + return scalarSQL(s.T(), s.ctx, s.client, `SELECT id FROM organizations WHERE name = $1`, name) +} + +func (s *OrgInvoicesRepositoryPGTestSuite) customer(name, orgName string) { + s.T().Helper() + s.exec(`INSERT INTO billing_customers (org_id, provider_id, name, email) + VALUES ($1, $2, $2, $2)`, s.orgID(orgName), name) +} + +func (s *OrgInvoicesRepositoryPGTestSuite) invoice(customer, hostedURL, state string) { + s.T().Helper() + s.exec(`INSERT INTO billing_invoices (customer_id, provider_id, amount, currency, hosted_url, state) + VALUES ((SELECT id FROM billing_customers WHERE name = $1), $2, 100, 'usd', $2, $3)`, + customer, hostedURL, state) +} + +func (s *OrgInvoicesRepositoryPGTestSuite) links(orgName string) []string { + s.T().Helper() + res, err := s.repository.Search(s.ctx, s.orgID(orgName), &rql.Query{Limit: 50}) + s.Require().NoError(err) + out := make([]string, 0, len(res.Invoices)) + for _, i := range res.Invoices { + out = append(out, i.InvoiceLink) + } + return out +} + +func (s *OrgInvoicesRepositoryPGTestSuite) TestSkipsDeletedInvoicesCustomersAndOrgs() { + s.Equal([]string{"oi-inv-live"}, s.links("oi-live")) +} + +func (s *OrgInvoicesRepositoryPGTestSuite) TestSoftDeletedOrgHasNoInvoices() { + s.Empty(s.links("oi-gone")) +} + +func (s *OrgInvoicesRepositoryPGTestSuite) TestGroupCountsSkipDeletedRows() { + res, err := s.repository.Search(s.ctx, s.orgID("oi-live"), + &rql.Query{Limit: 50, GroupBy: []string{"state"}}) + s.Require().NoError(err) + s.Require().Len(res.Group.Data, 1) + s.Equal("paid", res.Group.Data[0].Name) + s.Equal(1, res.Group.Data[0].Count) +} + +func TestOrgInvoicesRepositoryPG(t *testing.T) { + suite.Run(t, new(OrgInvoicesRepositoryPGTestSuite)) +}