@stacksjs/commerce 0.75.432 → 0.75.443
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +14 -14
- package/dist/tests/fixtures/returning-all-writes.d.ts +0 -1
- package/dist/tests/fixtures/returning-all-writes.js +0 -1
- package/dist/tests/fixtures/soft-delete-counts.d.ts +0 -1
- package/dist/tests/fixtures/soft-delete-counts.js +0 -1
- package/dist/tests/fixtures/write-counts.d.ts +0 -1
- package/dist/tests/fixtures/write-counts.js +0 -8
- package/dist/tests/setup.d.ts +0 -5
- package/dist/tests/setup.js +0 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/commerce",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.75.
|
|
4
|
+
"version": "0.75.443",
|
|
5
5
|
"description": "Stacks commerce utilities.",
|
|
6
6
|
"author": "Chris Breuer",
|
|
7
7
|
"contributors": [
|
|
@@ -57,19 +57,19 @@
|
|
|
57
57
|
"prepublishOnly": "bun run build"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@stacksjs/cache": "0.75.
|
|
61
|
-
"@stacksjs/config": "0.75.
|
|
62
|
-
"@stacksjs/database": "0.75.
|
|
63
|
-
"@stacksjs/env": "0.75.
|
|
64
|
-
"@stacksjs/error-handling": "0.75.
|
|
65
|
-
"@stacksjs/events": "0.75.
|
|
66
|
-
"@stacksjs/logging": "0.75.
|
|
67
|
-
"@stacksjs/orm": "0.75.
|
|
68
|
-
"@stacksjs/payments": "0.75.
|
|
69
|
-
"@stacksjs/realtime": "0.75.
|
|
70
|
-
"@stacksjs/slug": "0.75.
|
|
71
|
-
"@stacksjs/strings": "0.75.
|
|
72
|
-
"@stacksjs/validation": "0.75.
|
|
60
|
+
"@stacksjs/cache": "0.75.443",
|
|
61
|
+
"@stacksjs/config": "0.75.443",
|
|
62
|
+
"@stacksjs/database": "0.75.443",
|
|
63
|
+
"@stacksjs/env": "0.75.443",
|
|
64
|
+
"@stacksjs/error-handling": "0.75.443",
|
|
65
|
+
"@stacksjs/events": "0.75.443",
|
|
66
|
+
"@stacksjs/logging": "0.75.443",
|
|
67
|
+
"@stacksjs/orm": "0.75.443",
|
|
68
|
+
"@stacksjs/payments": "0.75.443",
|
|
69
|
+
"@stacksjs/realtime": "0.75.443",
|
|
70
|
+
"@stacksjs/slug": "0.75.443",
|
|
71
|
+
"@stacksjs/strings": "0.75.443",
|
|
72
|
+
"@stacksjs/validation": "0.75.443",
|
|
73
73
|
"ts-spreadsheets": "^0.2.0"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import assert from"node:assert/strict";import{basename,dirname,join}from"node:path";import process from"node:process";const dialect=process.env.DB_CONNECTION;assert(dialect==="sqlite"||dialect==="postgres"||dialect==="mysql");const configPath=process.env.STACKS_COMMERCE_RETURNING_ALL_CONFIG;assert(configPath&&basename(dirname(configPath)).startsWith("stacks-commerce-returning-all-"));if(dialect==="sqlite")assert.equal(dirname(process.env.DB_DATABASE_PATH),dirname(configPath));else{assert.equal(process.env.DB_DATABASE_PATH,":memory:");assert(process.env.DB_DATABASE?.startsWith("stacks_commerce_returning_all_"));assert(["127.0.0.1","localhost","[::1]"].includes(process.env.DB_HOST))}const{overridesReady}=await import("@stacksjs/config");await overridesReady;const{db,ensureDatabaseConfigLoaded,initializeDbConfig,resetDatabaseConnection}=await import("@stacksjs/database");await ensureDatabaseConfigLoaded();initializeDbConfig({app:{env:"test"},database:{default:dialect,connections:dialect==="sqlite"?{sqlite:{database:process.env.DB_DATABASE_PATH}}:{[dialect]:{name:process.env.DB_DATABASE,host:process.env.DB_HOST,port:Number(process.env.DB_PORT),username:process.env.DB_USERNAME,password:process.env.DB_PASSWORD}},queryLogging:{enabled:!1}}});const TABLES=["carts","categories","customers","product_units","product_variants","products","shipping_methods","shipping_rates","shipping_zones"],{buildMigrationPlan,generateSql,loadModels}=await import("bun-query-builder"),modelsDir=join(import.meta.dir,"../../../../../defaults/app/Models"),models={...await loadModels({modelsDir}),...await loadModels({modelsDir:join(modelsDir,"commerce")})},plan=buildMigrationPlan(models,{dialect});plan.tables=plan.tables.filter((table)=>TABLES.includes(table.table));assert.deepEqual(plan.tables.map((table)=>table.table).sort(),TABLES);for(const table of plan.tables)for(const column of table.columns)delete column.references;const statements=generateSql(plan),{update:updateCart}=await import("../../carts/update"),{update:updateItem}=await import("../../products/items/update"),{update:updateVariant}=await import("../../products/variants/update"),{update:updateUnit}=await import("../../products/units/update"),{update:updateRate}=await import("../../shippings/shipping-rates/update"),{store:storeCategory}=await import("../../products/categories/store"),{store:storeCustomer}=await import("../../customers/store"),{bulkStore:bulkStoreItems,store:storeItem}=await import("../../products/items/store");async function stored(table,id){return await db.selectFrom(table).selectAll().where("id","=",id).executeTakeFirst()}const failures=[];async function check(name,run){try{await run();console.log(`PASS ${name}`)}catch(error){failures.push(`${name}: ${error instanceof Error?error.message:String(error)}`)}}try{for(const statement of statements)await db.unsafe(statement).execute();await db.insertInto("carts").values({id:1,status:"active",expires_at:"2030-02-01 00:00:00",applied_coupon_id:"none"}).execute();await db.insertInto("products").values({id:1,name:"before",slug:"before",price:100,preparation_time:5}).execute();await db.insertInto("product_variants").values({id:1,variant:"Size",type:"dropdown",status:"active"}).execute();await db.insertInto("product_units").values({id:1,name:"gram",abbreviation:"g",type:"weight"}).execute();await db.insertInto("shipping_methods").values({id:1,name:"standard",base_rate:500,status:"active"}).execute();await db.insertInto("shipping_zones").values({id:1,name:"domestic",status:"active",shipping_method_id:1}).execute();await db.insertInto("shipping_rates").values({id:1,weight_from:0,weight_to:10,rate:100,shipping_method_id:1,shipping_zone_id:1}).execute();if(dialect==="postgres")await db.unsafe(`SELECT setval(pg_get_serial_sequence('products', 'id'), (SELECT MAX("id") FROM "products"))`).execute();await check("carts update",async()=>{const row=await updateCart(1,{notes:"after"});assert.equal(row?.notes,"after",`${dialect}: the returned cart`);assert.equal((await stored("carts",1))?.notes,"after",`${dialect}: the stored cart`)});await check("product items update",async()=>{const row=await updateItem(1,{name:"after"});assert.equal(row?.name,"after",`${dialect}: the returned product`);assert.equal((await stored("products",1))?.name,"after",`${dialect}: the stored product`)});await check("product variants update",async()=>{const row=await updateVariant(1,{variant:"Colour"});assert.equal(row?.variant,"Colour",`${dialect}: the returned variant`);assert.equal((await stored("product_variants",1))?.variant,"Colour",`${dialect}: the stored variant`)});await check("product units update",async()=>{const row=await updateUnit(1,{name:"kilogram"});assert.equal(row?.name,"kilogram",`${dialect}: the returned unit`);assert.equal((await stored("product_units",1))?.name,"kilogram",`${dialect}: the stored unit`)});await check("shipping rates update",async()=>{const row=await updateRate(1,{rate:250});assert.equal(Number(row?.rate),250,`${dialect}: the returned rate`);assert.equal(Number((await stored("shipping_rates",1))?.rate),250,`${dialect}: the stored rate`)});await check("categories store",async()=>{const row=await storeCategory({name:"Shoes",slug:`shoes-${dialect}`,display_order:1});assert(row?.id,`${dialect}: the inserted category comes back with its id`);assert.equal(row.name,"Shoes");assert.equal((await stored("categories",row.id))?.slug,`shoes-${dialect}`,`${dialect}: the stored category`)});await check("customers store",async()=>{const row=await storeCustomer({name:"Ada",email:`ada-${dialect}@example.test`,phone:"+15555550100",status:"Active"});assert(row?.id,`${dialect}: the stored customer comes back with its id`);assert.equal((await stored("customers",row.id))?.email,`ada-${dialect}@example.test`,`${dialect}: the stored customer`)});await check("product items store gives each product a slug",async()=>{const first=await storeItem({name:"Trail Runner",price:100,preparation_time:5}),second=await storeItem({name:"Trail Runner",price:100,preparation_time:5});assert.equal(first?.slug,"trail-runner",`${dialect}: the first product's slug`);assert.equal(second?.slug,"trail-runner-2",`${dialect}: a second product with the same name`);const named=await storeItem({name:"Desk Lamp",slug:"reading-lamp",price:100,preparation_time:5});assert.equal(named?.slug,"reading-lamp",`${dialect}: a slug the caller chose is kept`);assert.equal(await bulkStoreItems([{name:"Trail Runner",price:100,preparation_time:5},{name:"Trail Runner",price:100,preparation_time:5}]),2,`${dialect}: both bulk products are written`);const slugs=(await db.selectFrom("products").select("slug").where("name","=","Trail Runner").execute()).map((row)=>row.slug).sort();assert.deepEqual(slugs,["trail-runner","trail-runner-2","trail-runner-3","trail-runner-4"],`${dialect}: every Trail Runner has its own slug`)});assert.deepEqual(failures,[],`${dialect}: every returningAll() write must apply and return its row`);console.log("commerce returningAll writes OK")}finally{resetDatabaseConnection()}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import assert from"node:assert/strict";import{basename,dirname,join}from"node:path";import process from"node:process";const dialect=process.env.DB_CONNECTION;assert(dialect==="sqlite"||dialect==="postgres"||dialect==="mysql");const configPath=process.env.STACKS_COMMERCE_SOFT_DELETE_CONFIG;assert(configPath&&basename(dirname(configPath)).startsWith("stacks-commerce-soft-delete-"));if(dialect==="sqlite")assert.equal(dirname(process.env.DB_DATABASE_PATH),dirname(configPath));else{assert.equal(process.env.DB_DATABASE_PATH,":memory:");assert(process.env.DB_DATABASE?.startsWith("stacks_commerce_soft_delete_"));assert(["127.0.0.1","localhost","[::1]"].includes(process.env.DB_HOST))}const{overridesReady}=await import("@stacksjs/config");await overridesReady;const{db,ensureDatabaseConfigLoaded,initializeDbConfig,resetDatabaseConnection}=await import("@stacksjs/database");await ensureDatabaseConfigLoaded();initializeDbConfig({app:{env:"test"},database:{default:dialect,connections:dialect==="sqlite"?{sqlite:{database:process.env.DB_DATABASE_PATH}}:{[dialect]:{name:process.env.DB_DATABASE,host:process.env.DB_HOST,port:Number(process.env.DB_PORT),username:process.env.DB_USERNAME,password:process.env.DB_PASSWORD}},queryLogging:{enabled:!1}}});const TABLES=["carts","digital_deliveries","gift_cards","license_keys","orders","product_units","product_variants","products","shipping_methods","shipping_rates","shipping_zones"],{buildMigrationPlan,generateSql,loadModels}=await import("bun-query-builder"),modelsDir=join(import.meta.dir,"../../../../../defaults/app/Models"),models={...await loadModels({modelsDir}),...await loadModels({modelsDir:join(modelsDir,"commerce")})},plan=buildMigrationPlan(models,{dialect});plan.tables=plan.tables.filter((table)=>TABLES.includes(table.table));assert.deepEqual(plan.tables.map((table)=>table.table).sort(),TABLES);for(const table of plan.tables)for(const column of table.columns)delete column.references;const statements=generateSql(plan),orders=await import("../../orders/destroy"),digitalDeliveries=await import("../../shippings/digital-deliveries/destroy"),licenseKeys=await import("../../shippings/license-keys/destroy"),shippingMethods=await import("../../shippings/shipping-methods/destroy"),shippingZones=await import("../../shippings/shipping-zones/destroy"),{deactivate:deactivateGiftCard}=await import("../../gift-cards/destroy"),{bulkUpdate:bulkUpdateVariants,updateStatus:updateVariantStatus}=await import("../../products/variants/update"),{bulkUpdate:bulkUpdateCarts}=await import("../../carts/update"),{updateByZone:updateRatesByZone}=await import("../../shippings/shipping-rates/update"),{bulkUpdate:bulkUpdateItems}=await import("../../products/items/update"),{bulkUpdate:bulkUpdateUnits}=await import("../../products/units/update"),MODULES=[{name:"orders",table:"orders",deleted:"CANCELED",module:orders,row:(id,status)=>({id,status,total_amount:1000,order_type:"DELIVERY"}),live:"PENDING"},{name:"shippings/digital-deliveries",table:"digital_deliveries",deleted:"inactive",module:digitalDeliveries,row:(id,status)=>({id,status,name:`delivery-${id}`,description:"seeded",expiry_days:7}),live:"active"},{name:"shippings/license-keys",table:"license_keys",deleted:"inactive",module:licenseKeys,row:(id,status)=>({id,status,key:`KEY-${id}`,template:"Standard License",expiry_date:"2030-01-01"}),live:"active"},{name:"shippings/shipping-methods",table:"shipping_methods",deleted:"inactive",module:shippingMethods,row:(id,status)=>({id,status,name:`method-${id}`,base_rate:500}),live:"active"},{name:"shippings/shipping-zones",table:"shipping_zones",deleted:"inactive",module:shippingZones,row:(id,status)=>({id,status,name:`zone-${id}`}),live:"active"}];async function statusOf(table,id){return(await db.selectFrom(table).selectAll().where("id","=",id).executeTakeFirst())?.status}const failures=[];async function check(name,run){try{await run();console.log(`PASS ${name}`)}catch(error){failures.push(`${name}: ${error instanceof Error?error.message:String(error)}`)}}try{for(const statement of statements)await db.unsafe(statement).execute();for(const{name,table,deleted,live,module,row}of MODULES)await check(`${name} soft delete counts rows already in the target state`,async()=>{for(const[id,status]of[[1,deleted],[2,live],[3,deleted],[4,live]])await db.insertInto(table).values(row(id,status)).execute();assert.equal(await module.softDelete(1),!0,`${dialect}: soft-deleting an already soft-deleted row must report success`);assert.equal(await statusOf(table,1),deleted,"the row is still there, still soft-deleted");assert.equal(await module.softDelete(2),!0,`${dialect}: soft-deleting a live row must report success`);assert.equal(await statusOf(table,2),deleted);assert.equal(await module.bulkSoftDelete([3,4]),2,`${dialect}: bulk soft delete must count rows already in the target state`);assert.equal(await statusOf(table,3),deleted);assert.equal(await statusOf(table,4),deleted);assert.equal(await module.softDelete(404),!1,`${dialect}: soft-deleting a row that does not exist must report failure`);assert.equal(await module.bulkSoftDelete([404,405]),0)});const{setSystemTime}=await import("bun:test");setSystemTime(new Date("2030-01-02T03:04:05.000Z"));try{await check("gift card deactivate is idempotent within one second",async()=>{await db.insertInto("gift_cards").values({id:1,code:"GC-1",initial_balance:5000,current_balance:5000,status:"ACTIVE"}).execute();assert.equal(await deactivateGiftCard(1),!0,`${dialect}: deactivating an active gift card must report success`);assert.equal(await deactivateGiftCard(1),!0,`${dialect}: deactivating an already deactivated gift card must report success`);assert.equal(await statusOf("gift_cards",1),"DEACTIVATED")});await check("variant updateStatus reports success for the status it already has",async()=>{await db.insertInto("product_variants").values({id:1,variant:"Size",type:"dropdown",status:"active"}).execute();await db.insertInto("product_variants").values({id:2,variant:"Colour",type:"dropdown",status:"draft"}).execute();assert.equal(await updateVariantStatus(1,"active"),!0,`${dialect}: re-applying a variant's current status must report success`);assert.equal(await updateVariantStatus(1,"active"),!0,`${dialect}: re-applying it again in the same second must report success`);assert.equal(await updateVariantStatus(2,"active"),!0,`${dialect}: changing a variant's status must report success`);assert.equal(await statusOf("product_variants",2),"active");assert.equal(await updateVariantStatus(404,"active"),!1,`${dialect}: a variant that does not exist must report failure`)});await check("variant bulkUpdate counts rows already holding the values",async()=>{const rows=await db.selectFrom("product_variants").selectAll().where("id","in",[1,2]).execute();assert.equal(rows.length,2);const resaved=await bulkUpdateVariants(rows.map((row)=>({id:row.id,status:row.status})));assert.equal(resaved,2,`${dialect}: bulk update must count rows that already hold the values written`)});await check("cart bulkUpdate counts rows already holding the values, inside a transaction",async()=>{for(const id of[1,2])await db.insertInto("carts").values({id,status:"active",expires_at:"2030-02-01 00:00:00",applied_coupon_id:"none"}).execute();const rows=[{id:1,status:"active"},{id:2,status:"active"}];assert.equal(await bulkUpdateCarts(rows),2);assert.equal(await bulkUpdateCarts(rows),2,`${dialect}: bulk update must count carts that already hold the values written`)});await check("shipping rate updateByZone counts rates already at that rate",async()=>{for(const id of[1,2])await db.insertInto("shipping_rates").values({id,weight_from:0,weight_to:10,rate:250,shipping_zone_id:7}).execute();assert.equal(await updateRatesByZone(7,{rate:250}),2);assert.equal(await updateRatesByZone(7,{rate:250}),2,`${dialect}: updateByZone must count rates already at the requested rate`)});await check("product bulkUpdate counts rows already holding the values",async()=>{for(const id of[1,2])await db.insertInto("products").values({id,name:`product-${id}`,slug:`product-${id}`,price:100,preparation_time:5}).execute();const rows=[{id:1,name:"product-1"},{id:2,name:"product-2"}];assert.equal(await bulkUpdateItems(rows),2);assert.equal(await bulkUpdateItems(rows),2,`${dialect}: bulk update must count products that already hold the values written`)});await check("product unit bulkUpdate counts rows already holding the values",async()=>{for(const id of[1,2])await db.insertInto("product_units").values({id,name:`unit-${id}`,abbreviation:`u${id}`,type:"weight"}).execute();const rows=[{id:1,name:"unit-1"},{id:2,name:"unit-2"}];assert.equal(await bulkUpdateUnits(rows),2);assert.equal(await bulkUpdateUnits(rows),2,`${dialect}: bulk update must count units that already hold the values written`)})}finally{setSystemTime()}assert.deepEqual(failures,[],`${dialect}: every write must report the rows its predicate matched`);console.log("commerce soft delete counts OK")}finally{resetDatabaseConnection()}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import assert from"node:assert/strict";import{tmpdir}from"node:os";import{basename,dirname,join}from"node:path";import process from"node:process";const dialect=process.env.DB_CONNECTION;assert(dialect==="sqlite"||dialect==="postgres"||dialect==="mysql");const configPath=process.env.STACKS_COMMERCE_WRITE_COUNTS_CONFIG;assert(configPath&&basename(dirname(configPath)).startsWith("stacks-commerce-write-counts-"));if(dialect==="sqlite")assert.equal(dirname(process.env.DB_DATABASE_PATH),dirname(configPath));else{assert.equal(process.env.DB_DATABASE_PATH,":memory:");assert(process.env.DB_DATABASE?.startsWith("stacks_commerce_write_counts_"));assert(["127.0.0.1","localhost","[::1]"].includes(process.env.DB_HOST))}const{overridesReady}=await import("@stacksjs/config");await overridesReady;const{Database}=await import("bun:sqlite"),{db,ensureDatabaseConfigLoaded,initializeDbConfig,resetDatabaseConnection,sqlDateTime}=await import("@stacksjs/database");await ensureDatabaseConfigLoaded();initializeDbConfig({app:{env:"test"},database:{default:dialect,connections:dialect==="sqlite"?{sqlite:{database:process.env.DB_DATABASE_PATH}}:{[dialect]:{name:process.env.DB_DATABASE,host:process.env.DB_HOST,port:Number(process.env.DB_PORT),username:process.env.DB_USERNAME,password:process.env.DB_PASSWORD}},queryLogging:{enabled:!1}}});const TABLES=["carts","categories","coupons","gift_cards","license_keys","payments","receipts"],{buildMigrationPlan,generateSql,loadModels}=await import("bun-query-builder"),modelsDir=join(import.meta.dir,"../../../../../defaults/app/Models"),models={...await loadModels({modelsDir}),...await loadModels({modelsDir:join(modelsDir,"commerce")})},plan=buildMigrationPlan(models,{dialect});plan.tables=plan.tables.filter((table)=>TABLES.includes(table.table));assert.deepEqual(plan.tables.map((table)=>table.table).sort(),TABLES);for(const table of plan.tables)for(const column of table.columns)delete column.references;const statements=generateSql(plan),{deactivate,deactivateChildCategories}=await import("../../products/categories/destroy"),{fetchStats:fetchCategoryStats}=await import("../../products/categories/fetch"),{registerPersistentQueryHooks}=await import("@stacksjs/query-builder"),{redeem}=await import("../../coupons/update"),{store:storeCoupon}=await import("../../coupons/store"),{updateBalance}=await import("../../gift-cards/update"),{store:storeGiftCard}=await import("../../gift-cards/store"),{update:updateGiftCard}=await import("../../gift-cards/update"),{store:storeLicenseKey}=await import("../../shippings/license-keys/store"),{update:updateLicenseKey}=await import("../../shippings/license-keys/update"),{parseSqlDateTime}=await import("@stacksjs/database"),{cleanupAbandonedCarts}=await import("../../orders/guards"),{recordRefund}=await import("../../payments/update"),{store:storePayment}=await import("../../payments/store"),{bulkStore}=await import("../../receipts/store");async function count(table){return(await db.selectFrom(table).selectAll().execute()).length}async function column(table,id,name){return(await db.selectFrom(table).selectAll().where("id","=",id).executeTakeFirst())?.[name]}function deleteOnAnotherConnection(id){if(dialect==="sqlite"){const handle=new Database(process.env.DB_DATABASE_PATH);try{handle.run("DELETE FROM categories WHERE id = ?",[id])}finally{handle.close()}return!0}const adapter=dialect==="postgres"?"postgres":"mysql",child=Bun.spawnSync([process.execPath,"-e",`
|
|
2
|
-
const db = new Bun.SQL({ adapter: '${adapter}', hostname: process.env.DB_HOST,
|
|
3
|
-
port: Number(process.env.DB_PORT), database: process.env.DB_DATABASE,
|
|
4
|
-
username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD,
|
|
5
|
-
tls: process.env.DB_SSL === 'true' ? 'require' : 'disable' })
|
|
6
|
-
try { await db.unsafe('DELETE FROM categories WHERE id = ${id}') }
|
|
7
|
-
finally { await db.close() }
|
|
8
|
-
`],{cwd:tmpdir(),env:process.env,stdout:"pipe",stderr:"pipe",timeout:5000});if(child.exitCode!==0)console.error(new TextDecoder().decode(child.stderr));return child.exitCode===0}const failures=[];async function check(name,run){try{await run();console.log(`PASS ${name}`)}catch(error){failures.push(`${name}: ${error instanceof Error?error.message:String(error)}`)}}try{for(const statement of statements)await db.unsafe(statement).execute();const today=new Date;await check("orders cleanupAbandonedCarts",async()=>{const old="2020-01-01 00:00:00",fresh=today.toISOString().slice(0,19).replace("T"," ");for(const[index,updatedAt]of[old,old,fresh].entries())await db.insertInto("carts").values({uuid:`cart-${index}`,applied_coupon_id:"",expires_at:updatedAt,created_at:updatedAt,updated_at:updatedAt}).execute();const before=await count("carts"),{deleted}=await cleanupAbandonedCarts({olderThanDays:30}),removed=before-await count("carts");assert.equal(removed,2,"the two abandoned carts were deleted");assert.equal(deleted,removed,`cleanupAbandonedCarts reported ${deleted} but removed ${removed} carts`)});await check("coupons redeem",async()=>{const today=new Date,day=(offset)=>new Date(today.getTime()+offset*86400000).toISOString().slice(0,10),coupon=await storeCoupon({code:`REDEEM-${dialect}`,discount_type:"percentage",discount_value:10,product_id:1,start_date:day(-1),end_date:day(30),is_active:!0,usage_limit:2});assert(coupon?.id,"seed coupon");const id=Number(coupon.id),first=await redeem(id);assert.equal(Number(await column("coupons",id,"usage_count")),1,"the redemption was committed");assert.equal(first.ok,!0,`redeem reported ${JSON.stringify(first)} for a redemption it committed`);assert.equal((await redeem(id)).ok,!0);const third=await redeem(id);assert.equal(third.ok,!1);assert.equal(third.ok===!1&&third.reason,"limit-reached");assert.equal(Number(await column("coupons",id,"usage_count")),2,"a rejected redemption must not count")});await check("gift cards updateBalance",async()=>{const card=await storeGiftCard({code:`GC-WRITE-COUNTS-${dialect}`,initial_balance:100,current_balance:100,currency:"USD",status:"ACTIVE",is_active:!0});assert(card?.id,"seed gift card");const spent=await updateBalance(Number(card.id),-40);assert.equal(Number(await column("gift_cards",card.id,"current_balance")),60);assert.equal(Number(spent?.current_balance),60);const used=await updateBalance(Number(card.id),-60);assert.equal(await column("gift_cards",card.id,"status"),"USED");assert.equal(used?.status,"USED");await assert.rejects(updateBalance(Number(card.id),-1),/not active/,"a spent card rejects another redemption")});await check("gift cards updateBalance by zero",async()=>{const card=await storeGiftCard({code:`GC-ZERO-${dialect}`,initial_balance:100,current_balance:100,currency:"USD",status:"ACTIVE",is_active:!0});assert(card?.id,"seed gift card");const{setSystemTime}=await import("bun:test");setSystemTime(new Date("2030-01-02T03:04:05.000Z"));try{await updateBalance(Number(card.id),0);const again=await updateBalance(Number(card.id),0);assert.equal(Number(again?.current_balance),100,`${dialect}: a zero adjustment must return the card unchanged`);await assert.rejects(updateBalance(Number(card.id),-1000),/Insufficient/,"an over-spend is still insufficient");assert.equal(Number(await column("gift_cards",card.id,"current_balance")),100)}finally{setSystemTime()}});await check("gift cards updateBalance on an expired card",async()=>{const card=await storeGiftCard({code:`GC-EXPIRED-${dialect}`,initial_balance:100,current_balance:100,currency:"USD",status:"ACTIVE",is_active:!0,expiry_date:"2020-01-01 00:00:00"});assert(card?.id,"seed gift card");await assert.rejects(updateBalance(Number(card.id),-10),/expired/,`${dialect}: a redemption on an expired card must say it expired`);await assert.rejects(updateBalance(Number(card.id),0),/expired/,`${dialect}: a zero adjustment on an expired card must be refused, not reported as success`);assert.equal(Number(await column("gift_cards",card.id,"current_balance")),100)});await check("gift card and license key dates sent as Unix seconds",async()=>{const stored=async(table,id,name)=>parseSqlDateTime(await column(table,id,name))?.toISOString(),card=await storeGiftCard({code:`GC-DATES-${dialect}`,initial_balance:50,current_balance:50,currency:"USD",status:"ACTIVE",is_active:!0,expiry_date:1798761600});assert(card?.id,"gift card with a seconds expiry");assert.equal(await stored("gift_cards",card.id,"expiry_date"),"2027-01-01T00:00:00.000Z");await updateGiftCard(Number(card.id),{expiry_date:1830297600});assert.equal(await stored("gift_cards",card.id,"expiry_date"),"2028-01-01T00:00:00.000Z");const key=await storeLicenseKey({key:"ABCD-EFGH-IJKL-MNOP-QRST",template:"Standard License",status:"unassigned",expiry_date:1798761600});assert(key?.id,"license key with a seconds expiry");assert.equal(await stored("license_keys",key.id,"expiry_date"),"2027-01-01T00:00:00.000Z");await updateLicenseKey(Number(key.id),{status:"active"});assert.equal(await stored("license_keys",key.id,"expiry_date"),"2027-01-01T00:00:00.000Z")});await check("payments recordRefund",async()=>{const payment=await storePayment({amount:1000,method:"creditCard",status:"completed",transaction_id:`TXN-WRITE-COUNTS-${dialect}`});assert(payment?.id,"seed payment");const partial=await recordRefund(Number(payment.id),250);assert.equal(Number(await column("payments",payment.id,"refund_amount")),250);assert.equal(partial.status,"partiallyRefunded");const full=await recordRefund(Number(payment.id),750);assert.equal(await column("payments",payment.id,"status"),"refunded");assert.equal(full.status,"refunded");await assert.rejects(recordRefund(Number(payment.id),1),/cannot be refunded/,"a fully refunded payment rejects another refund")});await check("product categories deactivate",async()=>{let next=0;const seedCategory=async(name,parentId)=>{next+=1;await db.insertInto("categories").values({uuid:crypto.randomUUID(),name,slug:`${name}-${dialect}-${next}`,display_order:next,is_active:!0,parent_category_id:parentId,created_at:sqlDateTime(),updated_at:sqlDateTime()}).execute();const rows=await db.selectFrom("categories").selectAll().execute();return Number(rows[rows.length-1].id)},id=await seedCategory("parent",null);assert.equal(await deactivate(id),!0,"deactivating a category that exists");assert.equal(Boolean(await column("categories",id,"is_active")),!1);assert.equal(await deactivate(id),!0,"deactivating a category that is already inactive");for(const child of["a","b"])await seedCategory(`child-${child}`,id);const{setSystemTime}=await import("bun:test");setSystemTime(new Date("2030-01-02T03:04:05.000Z"));try{assert.equal(await deactivateChildCategories(String(id)),2);assert.equal(await deactivateChildCategories(String(id)),2,`${dialect}: children that are already inactive are still the children deactivated`)}finally{setSystemTime()}const doomed=await seedCategory("doomed",null);let removed=!1;const unregister=registerPersistentQueryHooks({onQueryStart(event){if(event.kind!=="update"||removed)return;removed=deleteOnAnotherConnection(doomed)}});try{const claimed=await deactivate(doomed);assert.equal(removed,!0,"the competing delete must have run");assert.equal(claimed,!1,`${dialect}: deactivate must not report success for a row that is gone`)}finally{unregister()}assert.equal(await db.selectFrom("categories").selectAll().where("id","=",doomed).executeTakeFirst(),void 0)});await check("category stats name the parent with the most children",async()=>{const insert=async(name,parentId)=>{await db.insertInto("categories").values({uuid:crypto.randomUUID(),name,slug:`${name}-${dialect}`,display_order:1,is_active:!0,parent_category_id:parentId,created_at:sqlDateTime(),updated_at:sqlDateTime()}).execute();const row=await db.selectFrom("categories").selectAll().where("name","=",name).executeTakeFirst();return Number(row.id)},parent=await insert("stats-parent",null);for(const child of["x","y","z"])await insert(`stats-child-${child}`,parent);const stats=await fetchCategoryStats();assert.deepEqual(stats.top_parent_categories[0],{id:String(parent),name:"stats-parent",child_count:3})});await check("receipts bulkStore",async()=>{const receipt=(index)=>({printer:"Front Counter",document:`Receipt #${index}`,timestamp:Date.parse("2026-01-10T12:00:00Z"),status:"success",size:12,pages:1,duration:2}),before=await count("receipts"),reported=await bulkStore([receipt(1),receipt(2),receipt(3)]),inserted=await count("receipts")-before;assert.equal(inserted,3,"the three receipts were inserted");assert.equal(reported,inserted,`bulkStore reported ${reported} but inserted ${inserted} receipts`)});assert.deepEqual(failures,[],`${dialect}: every commerce write must apply and report the rows it changed`);console.log("commerce write counts OK")}finally{resetDatabaseConnection()}
|
package/dist/tests/setup.d.ts
DELETED
package/dist/tests/setup.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{existsSync,unlinkSync}from"node:fs";import{tmpdir}from"node:os";import{join}from"node:path";import process from"node:process";const DB_PATH=join(tmpdir(),`stacks-commerce-${process.pid}.sqlite`);process.env.DB_CONNECTION="sqlite";process.env.DB_DATABASE_PATH=DB_PATH;process.env.APP_ENV="test";const{acquireDbConfigLock,db,ensureDatabaseConfigLoaded,initializeDbConfig}=await import("@stacksjs/database"),{buildMigrationPlan,generateSql,loadModels}=await import("bun-query-builder"),defaultsModelsDir=join(import.meta.dir,"../../../../defaults/app/Models"),commerceModelsDir=join(defaultsModelsDir,"commerce");async function forceConfig(){const releaseDbConfigLock=await acquireDbConfigLock();try{await ensureDatabaseConfigLoaded();initializeDbConfig({app:{env:"test"},database:{default:"sqlite",connections:{sqlite:{database:DB_PATH,prefix:""}}}})}finally{releaseDbConfigLock()}}for(const suffix of["","-wal","-shm"])if(existsSync(`${DB_PATH}${suffix}`))unlinkSync(`${DB_PATH}${suffix}`);await forceConfig();const models={...await loadModels({modelsDir:defaultsModelsDir}),...await loadModels({modelsDir:commerceModelsDir})},plan=buildMigrationPlan(models,{dialect:"sqlite"});for(const table of plan.tables)for(const column of table.columns)delete column.references;const statements=generateSql(plan);for(const statement of statements)await db.unsafe(statement).execute();const tableNames=plan.tables.map((table)=>table.table);export async function refreshDatabase(){await forceConfig();for(const table of tableNames)await db.unsafe(`DELETE FROM ${table}`).execute()}process.on("exit",()=>{for(const suffix of["","-wal","-shm"])try{if(existsSync(`${DB_PATH}${suffix}`))unlinkSync(`${DB_PATH}${suffix}`)}catch{}});
|