Commit

sudo: the billing reset no longer names sandbox_months (dropped in 0015), checked against the migrations by a test; a failed reset says why instead of an error page

syntaqxcommitted Parent2837513Browse files
3 files+43−50/3 viewed
+4−2
1212 import { FORGIVE_COST_MICROS, goodwillWarning } from "./pricing";
1313 import type { ActionData } from "./review";
1414 import { admin, identity, priceBook } from "./services.server";
15+import { settle } from "./settle";
1516 import type { Staff } from "./staff";
1617 import type { Enterprise } from "./workspaces";
1718
131132 if (values.confirmation !== subject.slug) {
132133 return failed("reset", `Type the workspace's slug, ${subject.slug}, exactly, to reset it.`, { ...values, confirmation: "" });
133134 }
134− const result = await admin.resetBilling(subject.slug, values.confirmation, note.value, staff.email);
135− if (!result.ok) return failed("reset", result.error.message, values);
135+ const result = await settle(admin.resetBilling(subject.slug, values.confirmation, note.value, staff.email));
136+ if (!result.ok) return failed("reset", `Billing did not answer: ${result.error}`, values);
137+ if (!result.value.ok) return failed("reset", result.value.error.message, values);
136138 return back("reset");
137139 }
138140
+1−1
356356 deletes the workspace's rows from every billing table: ledger and balance,
357357 plan and plan payments, limits and limit requests, trial grant, invoices,
358358 holds, card checks, alerts sent, price notices, month-end snapshots and
359−closes, storage and sandbox meters, token usage, spikes, sales records and
359+closes, storage meters, token usage, spikes, sales records and
360360 notes, `workspace_costs`, its workspace margin alert and its own billing
361361 account. It keeps `own_counts` (what Cloudflare's bill is compared with)
362362 and the audit log, which records the reset with the note and the number of
+38−2
4141 "DELETE FROM month_closes WHERE workspace = ?1",
4242 "DELETE FROM storage_days WHERE workspace = ?1",
4343 "DELETE FROM package_storage_days WHERE workspace = ?1",
44− "DELETE FROM sandbox_months WHERE workspace = ?1",
4544 "DELETE FROM token_usage WHERE workspace = ?1",
4645 "DELETE FROM spikes WHERE workspace = ?1",
4746 "DELETE FROM closed_workspaces WHERE workspace = ?1",
124123 "ledger", "runs", "checkouts", "workspace_invoices", "workspace_invoice_lines", "sales_notes", "accounts",
125124 "pending_usage", "pending_days", "limits", "subscriptions", "month_closes", "sales_records",
126125 "billing_accounts", "allowance_use", "trial_grants", "storage_days", "package_storage_days",
127− "sandbox_months", "token_usage", "reservations", "spikes", "limit_requests", "plan_payments",
126+ "token_usage", "reservations", "spikes", "limit_requests", "plan_payments",
128127 "card_checks", "alerts_sent", "price_notices", "closed_workspaces", "workspace_costs",
129128 "margin_alerts", "budget_alerts",
130129 ] {
133132 }
134133 }
135134
135+ /// The tables the migrations leave: every one made, less those dropped.
136+ fn live_tables() -> std::collections::BTreeSet<String> {
137+ let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("migrations");
138+ let mut files: Vec<_> = std::fs::read_dir(dir).unwrap().map(|e| e.unwrap().path()).collect();
139+ files.sort();
140+ let mut live = std::collections::BTreeSet::new();
141+ for file in files {
142+ let sql = std::fs::read_to_string(file).unwrap();
143+ for line in sql.lines().map(str::trim) {
144+ let words: Vec<&str> = line.split(|c: char| c.is_whitespace() || c == '(' || c == ';').filter(|w| !w.is_empty()).collect();
145+ let name = |at: usize| words.get(at).map(|w| w.to_string());
146+ match words.as_slice() {
147+ ["CREATE", "TABLE", "IF", "NOT", "EXISTS", ..] => live.extend(name(5)),
148+ ["CREATE", "TABLE", ..] => live.extend(name(2)),
149+ ["DROP", "TABLE", "IF", "EXISTS", ..] => {
150+ name(4).map(|n| live.remove(&n));
151+ }
152+ ["DROP", "TABLE", ..] => {
153+ name(2).map(|n| live.remove(&n));
154+ }
155+ _ => {}
156+ }
157+ }
158+ }
159+ live
160+ }
161+
162+ #[test]
163+ fn every_table_wiped_is_one_the_migrations_leave() {
164+ let live = live_tables();
165+ assert!(live.contains("ledger") && !live.contains("sandbox_months"), "{live:?}");
166+ for sql in STATEMENTS {
167+ let table = sql.split_whitespace().nth(2).unwrap();
168+ assert!(live.contains(table), "{table} is not a table after the migrations");
169+ }
170+ }
171+
136172 #[test]
137173 fn statements_name_at_most_the_workspace_and_its_account() {
138174 assert!(STATEMENTS.iter().all(|sql| crate::rename::parameters(sql) <= 2));