Files
atlas-seller-kit/apps/api/test/pricing.e2e.test.ts
T

726 lines
31 KiB
TypeScript

import assert from 'node:assert/strict';
import { randomUUID } from 'node:crypto';
import type { AddressInfo } from 'node:net';
import type { Database } from '@atlas/database';
import type { NestExpressApplication } from '@nestjs/platform-express';
import type {
PricingPreviewRequest,
ProductEditorSnapshot,
SaveProductEditorDraftRequest,
} from '@atlas/contracts';
import { pricingPreviewRequestSchema } from '@atlas/contracts';
import { beforeAll, afterAll, it, describe } from 'vitest';
import { DATABASE } from '../src/database/database.module.js';
import { createApplication } from '../src/main.js';
let app: NestExpressApplication,
db: Database,
base = '',
workspace = '',
user = '',
token = '',
platform = '',
store = '',
category = '',
product = '',
profile = '',
provider = '',
card = '',
transport = '',
variant = '';
async function request(
path: string,
method = 'GET',
body?: unknown,
extra: Record<string, string> = {},
) {
const r = await fetch(base + '/api' + path, {
method,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
...(method !== 'GET' ? { 'Idempotency-Key': randomUUID() } : {}),
...extra,
},
body: body === undefined ? undefined : JSON.stringify(body),
});
const data = (await r.json()) as any;
return { status: r.status, ...data };
}
async function read(): Promise<ProductEditorSnapshot> {
const r = await request(`/products/${product}/editor`, 'GET', undefined, {
'X-Atlas-Platform-Id': platform,
});
assert.equal(r.status, 200, JSON.stringify(r));
return r.data;
}
function draft(s: ProductEditorSnapshot): SaveProductEditorDraftRequest {
return {
ai_source_material: s.source_material,
ai_source_overridden_fields: s.source_material_state.overridden_fields,
common: {
...s.product.common,
default_physical_spec: s.product.default_physical_spec,
default_package_spec: {
shipping_weight_g: '380',
length_mm: '220',
width_mm: '160',
height_mm: '90',
status: 'confirmed',
},
},
profile: {
target_store_id: store,
fulfillment: 'rfbs',
title: s.profile.title,
description: s.profile.description,
tags: s.profile.tags,
platform_product_category_id: category,
category_source: 'manual',
},
variants: s.variants.map((v) => ({
id: v.id,
client_key: null,
display_name: v.display_name,
source_sku: v.source_sku,
purchase_cost_cny: '28.00',
specification_values: v.specification_values,
title: v.title,
tags: v.tags,
description: v.description,
physical_spec: v.physical_spec,
package_spec: v.package_spec,
offer_id: v.listing.offer_id,
media_asset_ids: [],
pricing_draft: v.listing.pricing_draft ?? null,
})),
};
}
function quote(
variantId: string | null = variant,
clientKey: string | null = null,
): PricingPreviewRequest {
return pricingPreviewRequestSchema.parse({
store_id: store,
platform_product_category_id: category,
fulfillment: 'rfbs',
binding: {
profile_id: profile,
variant_id: variantId,
client_key: clientKey,
},
purchase_cost_cny: '28',
package: {
shipping_weight_g: '380',
length_mm: '220',
width_mm: '160',
height_mm: '90',
status: 'confirmed',
},
pricing: {
rate_card_id: card,
transport_method_ids: [transport],
default_transport_method_id: transport,
domestic_shipping_and_label_fee_cny: '3',
ad_rate: '0.03',
last_mile_rate: '0.02',
withdrawal_rate: '0.01',
other_rate: '0.02',
desired_markup_rate: '0.5',
list_price_discount_rate: '0.2',
temporary_commission_rate: '0.12',
fx: {
mode: 'one_time_override',
rate: '12.3',
reason: '仅集成测试',
},
},
});
}
async function save(
s: ProductEditorSnapshot,
d: SaveProductEditorDraftRequest,
key = randomUUID(),
) {
return request(`/products/${product}/editor-draft`, 'PUT', d, {
'X-Atlas-Platform-Id': platform,
'If-Match': `"${s.product.revision}:${s.profile.revision}"`,
'Idempotency-Key': key,
});
}
describe('SKU 试算与原子确认 HTTP / PostgreSQL', () => {
beforeAll(async () => {
Object.assign(process.env, {
NODE_ENV: 'test',
PRICING_ALLOW_DRAFT_RATES: 'true',
SWAGGER_ENABLED: 'false',
AUTH_REGISTER_IP_LIMIT: '100',
AUTH_REGISTER_EMAIL_LIMIT: '100',
});
app = (await createApplication()).app;
db = app.get(DATABASE);
await app.listen(0, '127.0.0.1');
base = `http://127.0.0.1:${(app.getHttpServer().address() as AddressInfo).port}`;
const reg = await request('/auth/register', 'POST', {
email: `pricing-${randomUUID()}@example.test`,
password: 'pricing-test-password-123',
password_confirmation: 'pricing-test-password-123',
});
assert.equal(reg.status, 201, JSON.stringify(reg));
workspace = reg.data.workspace.id;
user = reg.data.user.id;
token = reg.data.access_token;
platform = (await request('/platforms')).data[0].id;
await db.workspaceTransaction(workspace, async (tx) => {
store = (
await tx.query<{ id: string }>(
`INSERT INTO atlas.stores(workspace_id,platform_id,name,platform_client_id,currency_code,is_default,status,credential_status) VALUES($1,$2,'定价集成测试',$3,'CNY',true,'active','valid') RETURNING id`,
[workspace, platform, randomUUID()],
)
).rows[0].id;
category = (
await tx.query<{ id: string }>(
`INSERT INTO atlas.platform_product_categories(platform_id,external_category_id,external_type_id,name,path) VALUES($1,$2,'test','测试类目','定价测试') RETURNING id`,
[platform, randomUUID()],
)
).rows[0].id;
await tx.query(
`INSERT INTO atlas.platform_catalog_resource_states(platform_id,scope,scope_key,category_id,last_success_at,is_complete) VALUES($1,'attributes','attributes:' || $2::uuid,$2,now(),true)`,
[platform, category],
);
provider = (
await tx.query<{ id: string }>(
`INSERT INTO atlas.logistics_providers(code,name) VALUES($1,'仅自动化测试') RETURNING id`,
[randomUUID()],
)
).rows[0].id;
card = (
await tx.query<{ id: string }>(
`INSERT INTO atlas.logistics_rate_cards(provider_id,platform_id,fulfillment,version,source_name,status,effective_from,reviewed_at,reviewed_by_user_id) VALUES($1,$2,'rfbs','test-1','测试规则,不用于业务','active',now()-interval '1 day',now(),$3) RETURNING id`,
[provider, platform, user],
)
).rows[0].id;
transport = (
await tx.query<{ id: string }>(
`INSERT INTO atlas.logistics_transport_methods(rate_card_id,code,name) VALUES($1,'standard','标准测试') RETURNING id`,
[card],
)
).rows[0].id;
const type = (
await tx.query<{ id: string }>(
`INSERT INTO atlas.logistics_product_types(rate_card_id,code,name,merchandise_currency_code,max_merchandise_value,max_actual_weight_kg,max_sum_dimensions_cm,max_longest_side_cm,billing_basis) VALUES($1,'ExtraSmall','小包测试','CNY',135,0.5,90,60,'actual_only') RETURNING id`,
[card],
)
).rows[0].id;
await tx.query(
`INSERT INTO atlas.logistics_rate_rules(product_type_id,transport_method_id,fixed_fee_cny,per_billable_kg_fee_cny) VALUES($1,$2,3.12,36.4)`,
[type, transport],
);
});
const collection = await request('/collection-items', 'POST', {
title: '定价集成测试商品',
});
assert.equal(collection.status, 201);
product = collection.data.product_id;
const context = await request(
`/collection-items/${collection.data.collection_item.id}/editor-contexts`,
'POST',
undefined,
{ 'X-Atlas-Platform-Id': platform },
);
assert.equal(context.status, 201, JSON.stringify(context));
profile = context.data.profile_id;
variant = (await read()).variants[0].id;
});
afterAll(async () => {
if (db && workspace) {
await db.workspaceTransaction(workspace, async (tx) => {
// 仅清理本测试创建的租户,按外键依赖顺序删除。
for (const table of [
'idempotency_requests',
'pricing_previews',
'product_variant_listing_media',
'product_variant_platform_listings',
'pricing_quote_options',
'pricing_quotes',
'pricing_policy_transport_methods',
'pricing_policies',
'product_unmapped_attributes',
'product_attributes',
'product_variants',
'product_platform_profiles',
'products',
'collection_items',
'outbox_events',
'operations',
'stores',
])
await tx.query(
`DELETE FROM atlas.${table} WHERE workspace_id=$1`,
[workspace],
);
});
if (card)
await db.query(
'DELETE FROM atlas.logistics_rate_cards WHERE id=$1',
[card],
);
if (provider)
await db.query(
'DELETE FROM atlas.logistics_providers WHERE id=$1',
[provider],
);
if (category) {
await db.query(
'DELETE FROM atlas.platform_catalog_resource_states WHERE category_id=$1',
[category],
);
await db.query(
'DELETE FROM atlas.platform_product_categories WHERE id=$1',
[category],
);
}
await db.query(
'DELETE FROM atlas.workspace_memberships WHERE user_id=$1',
[user],
);
await db.query('DELETE FROM atlas.workspaces WHERE id=$1', [
workspace,
]);
await db.query('DELETE FROM atlas.users WHERE id=$1', [user]);
}
if (app) await app.close();
});
it('目录只返回审核规则,预览幂等且独立试算不写商品', async () => {
const catalog = await request(`/pricing/catalog?store_id=${store}`);
assert.equal(catalog.status, 200, JSON.stringify(catalog));
assert.ok(catalog.data.rate_cards.some((c: any) => c.id === card));
const categories = await request(
`/pricing/categories?store_id=${store}&q=定价测试`,
);
assert.equal(categories.status, 200, JSON.stringify(categories));
const input = quote(),
key = randomUUID(),
a = await request('/pricing/quote-previews', 'POST', input, {
'Idempotency-Key': key,
}),
b = await request('/pricing/quote-previews', 'POST', input, {
'Idempotency-Key': key,
});
assert.equal(a.status, 201, JSON.stringify(a));
assert.deepEqual(b.data, a.data);
assert.equal(a.data.options[0].selling_price, '77.44');
assert.equal((await read()).variants[0].listing.selling_price, null);
});
it('原子确认、重放、重读与同输入再保存保持快照', async () => {
const s = await read(),
d = draft(s),
input = quote(),
p = await request('/pricing/quote-previews', 'POST', input);
d.variants[0].pricing_draft = input.pricing;
d.variants[0].pricing_selection = {
preview_id: p.data.preview_id,
option_id: p.data.recommended_option_id,
input_hash: p.data.input_hash,
};
const key = randomUUID(),
saved = await save(s, d, key);
assert.equal(saved.status, 200, JSON.stringify(saved));
assert.deepEqual((await save(s, d, key)).data, saved.data);
const after = await read();
assert.equal(after.variants[0].listing.pricing_valid, true);
assert.equal(
after.variants[0].listing.pricing_result?.profit_cny,
'14.00',
);
assert.equal(Number(after.variants[0].listing.selling_price), 77.44);
const again = await save(after, draft(after));
assert.equal(again.status, 200, JSON.stringify(again));
assert.equal((await read()).variants[0].listing.pricing_valid, true);
});
it('输入/有效期变化返回 SKU 错误,整笔商品保存回滚;无报价允许保存', async () => {
const s = await read(),
d = draft(s),
input = quote(),
p = await request('/pricing/quote-previews', 'POST', input);
d.common.source_title = '不应被保存';
d.variants[0].pricing_draft = input.pricing;
d.variants[0].purchase_cost_cny = '29';
d.variants[0].pricing_selection = {
preview_id: p.data.preview_id,
option_id: p.data.recommended_option_id,
input_hash: p.data.input_hash,
};
const failed = await save(s, d);
assert.equal(failed.status, 409, JSON.stringify(failed));
assert.equal(failed.error.code, 'PRICING_PREVIEW_STALE');
assert.equal((await read()).product.revision, s.product.revision);
d.variants[0].purchase_cost_cny = '28';
await db.workspaceTransaction(workspace, (tx) =>
tx.query(
`UPDATE atlas.pricing_previews SET expires_at=now()-interval '1 second' WHERE id=$1`,
[p.data.preview_id],
),
);
assert.equal((await save(s, d)).status, 409);
d.variants[0].pricing_selection = null;
d.variants[0].purchase_cost_cny = '29';
assert.equal((await save(s, d)).status, 200);
assert.equal((await read()).variants[0].listing.pricing_valid, false);
});
it('新 SKU client_key 与真实 ID 在同一事务映射', async () => {
const s = await read(),
d = draft(s),
input = quote(null, 'new-pricing'),
p = await request('/pricing/quote-previews', 'POST', input);
d.variants.push({
...d.variants[0],
id: null,
client_key: 'new-pricing',
display_name: '新报价 SKU',
pricing_draft: input.pricing,
pricing_selection: {
preview_id: p.data.preview_id,
option_id: p.data.recommended_option_id,
input_hash: p.data.input_hash,
},
});
const saved = await save(s, d);
assert.equal(saved.status, 200, JSON.stringify(saved));
const after = await read(),
newId = saved.data.created_variants[0].id;
assert.equal(
after.variants.find((v) => v.id === newId)?.listing.pricing_valid,
true,
);
});
it('其他租户不能读取预览或使用门店;报价复合外键拒绝跨 SKU 引用', async () => {
const outsider = randomUUID();
await db.workspaceTransaction(outsider, async (tx) => {
assert.equal(
(
await tx.query(
'SELECT id FROM atlas.pricing_previews WHERE store_id=$1',
[store],
)
).rowCount,
0,
);
assert.equal(
(
await tx.query(
'SELECT id FROM atlas.pricing_quotes WHERE store_id=$1',
[store],
)
).rowCount,
0,
);
});
const s = await read();
await assert.rejects(() =>
db.workspaceTransaction(workspace, async (tx) => {
await tx.query(
`UPDATE atlas.product_variant_platform_listings SET pricing_quote_id=(SELECT pricing_quote_id FROM atlas.product_variant_platform_listings WHERE product_variant_id=$1) WHERE product_variant_id=$2`,
[s.variants[1].id, s.variants[0].id],
);
}),
);
});
it('方案 CRUD、版本冲突、默认与归档不改变已确认报价', async () => {
const body = {
name: '测试默认方案',
parameters: {
...quote().pricing,
fx: { mode: 'policy_fixed', rate: '12.3' },
},
allow_commission_override: true,
};
const c = await request(
`/stores/${store}/pricing-policies`,
'POST',
body,
);
assert.equal(c.status, 201, JSON.stringify(c));
const id = c.data.id;
assert.equal(
(await request(`/pricing-policies/${id}/set-default`, 'POST'))
.status,
200,
);
assert.equal(
(
await request(`/pricing-policies/${id}`, 'PATCH', body, {
'If-Match': '"1"',
})
).status,
409,
);
const before = await read();
assert.equal(
(await request(`/pricing-policies/${id}`, 'DELETE')).status,
200,
);
assert.deepEqual(
(await read()).variants[1].listing.pricing_result,
before.variants[1].listing.pricing_result,
);
});
it('规则更新、独立预览和其他 SKU 预览不能被旧确认意图接受', async () => {
const state = await read(),
payload = draft(state),
input = quote();
const preview = await request('/pricing/quote-previews', 'POST', input);
payload.variants[0].pricing_draft = input.pricing;
payload.variants[0].pricing_selection = {
preview_id: preview.data.preview_id,
option_id: preview.data.recommended_option_id,
input_hash: preview.data.input_hash,
};
await db.query(
`UPDATE atlas.logistics_rate_cards SET version='test-2',updated_at=now() WHERE id=$1`,
[card],
);
assert.equal(
(await save(state, payload)).error.code,
'PRICING_PREVIEW_STALE',
);
const standalone = await request('/pricing/quote-previews', 'POST', {
...input,
binding: null,
});
payload.variants[0].pricing_selection = {
preview_id: standalone.data.preview_id,
option_id: standalone.data.recommended_option_id,
input_hash: standalone.data.input_hash,
};
assert.equal((await save(state, payload)).status, 409);
const other = await request('/pricing/quote-previews', 'POST', {
...input,
binding: { ...input.binding, variant_id: state.variants[1].id },
});
payload.variants[0].pricing_selection = {
preview_id: other.data.preview_id,
option_id: other.data.recommended_option_id,
input_hash: other.data.input_hash,
};
assert.equal((await save(state, payload)).status, 409);
assert.equal((await read()).product.revision, state.product.revision);
});
it('确认快照不可更新;历史预览过期不使确认失效', async () => {
const before = await read();
await assert.rejects(() =>
db.workspaceTransaction(workspace, (tx) =>
tx.query(
`UPDATE atlas.pricing_quotes SET purchase_cost_cny=0 WHERE workspace_id=$1 AND status='confirmed'`,
[workspace],
),
),
);
await assert.rejects(() =>
db.workspaceTransaction(workspace, (tx) =>
tx.query(
`UPDATE atlas.pricing_quote_options SET profit_rub=0 WHERE workspace_id=$1`,
[workspace],
),
),
);
await db.workspaceTransaction(workspace, (tx) =>
tx.query(
`UPDATE atlas.pricing_previews SET expires_at=now()-interval '1 day' WHERE workspace_id=$1`,
[workspace],
),
);
assert.deepEqual(
(await read()).variants[1].listing,
before.variants[1].listing,
);
});
it('没有当前报价的草稿保存成功,但发布校验给出定价问题', async () => {
const state = await read(),
payload = draft(state);
payload.variants[0].purchase_cost_cny = '41.00';
assert.equal((await save(state, payload)).status, 200);
const validation = await request(
`/product-platform-profiles/${profile}/validation`,
'GET',
undefined,
{ 'X-Atlas-Platform-Id': platform },
);
assert.equal(validation.status, 200, JSON.stringify(validation));
assert.ok(
validation.data.variants[0].issues.some(
(i: { code: string }) => i.code === 'PRICING_REQUIRED',
),
);
});
it('并发商品保存只有一个版本成功', async () => {
const state = await read(),
a = draft(state),
b = draft(state);
a.common.source_title = '并发 A';
b.common.source_title = '并发 B';
const results = await Promise.all([save(state, a), save(state, b)]);
assert.deepEqual(results.map((r) => r.status).sort(), [200, 409]);
});
it('未审核费率、缺失正式佣金以及亏损候选不会变成发布报价', async () => {
await db.query(
`UPDATE atlas.logistics_rate_cards SET reviewed_at=NULL WHERE id=$1`,
[card],
);
assert.equal(
(await request('/pricing/quote-previews', 'POST', quote())).error
.code,
'PRICING_RULES_UNAVAILABLE',
);
await db.query(
`UPDATE atlas.logistics_rate_cards SET reviewed_at=now() WHERE id=$1`,
[card],
);
const input = quote();
input.pricing.temporary_commission_rate = null;
const withoutCommission = await request(
'/pricing/quote-previews',
'POST',
input,
);
assert.equal(withoutCommission.error.code, 'PRICING_COMMISSION_UNAVAILABLE');
assert.match(withoutCommission.error.message, /本次自定义参数/);
input.pricing.temporary_commission_rate = '0.12';
input.pricing.mode = 'fixed_sale';
input.pricing.selling_price = '10';
const p = await request('/pricing/quote-previews', 'POST', input);
assert.equal(p.status, 201);
assert.equal(p.data.options[0].can_confirm, false);
const state = await read(),
payload = draft(state);
payload.variants[0].pricing_draft = input.pricing;
payload.variants[0].pricing_selection = {
preview_id: p.data.preview_id,
option_id: p.data.options[0].id,
input_hash: p.data.input_hash,
};
assert.equal((await save(state, payload)).status, 409);
});
it('草稿费率可试算、确认且不阻断首次上架预检', async () => {
await db.query(
`UPDATE atlas.logistics_rate_cards SET status='draft',reviewed_at=NULL,reviewed_by_user_id=NULL WHERE id=$1`,
[card],
);
await db.query(
`UPDATE atlas.logistics_rate_rules SET status='draft' WHERE product_type_id IN (SELECT id FROM atlas.logistics_product_types WHERE rate_card_id=$1)`,
[card],
);
const catalog = await request(`/pricing/catalog?store_id=${store}`);
assert.equal(catalog.status, 200, JSON.stringify(catalog));
assert.equal(
catalog.data.rate_cards.find((item: { id: string }) => item.id === card)
?.development_only,
true,
);
const input = quote();
input.purchase_cost_cny = '41.00';
const preview = await request('/pricing/quote-previews', 'POST', input);
assert.equal(preview.status, 201, JSON.stringify(preview));
assert.equal(preview.data.context.development_only, true);
const state = await read();
const payload = draft(state);
payload.variants[0].purchase_cost_cny = '41.00';
payload.variants[0].pricing_draft = input.pricing;
payload.variants[0].pricing_selection = {
preview_id: preview.data.preview_id,
option_id: preview.data.recommended_option_id,
input_hash: preview.data.input_hash,
};
const saved = await save(state, payload);
assert.equal(saved.status, 200, JSON.stringify(saved));
const validation = await request(
`/product-platform-profiles/${profile}/validation`,
'GET',
undefined,
{ 'X-Atlas-Platform-Id': platform },
);
assert.equal(validation.status, 200, JSON.stringify(validation));
assert.equal(
validation.data.variants[0].issues.some(
(issue: { code: string }) =>
issue.code === 'PRICING_RATE_REVIEW_REQUIRED' ||
issue.code === 'PRICING_REQUIRED',
),
false,
);
const rateCard = await db.query<{ status: string; reviewed_at: Date | null }>(
'SELECT status,reviewed_at FROM atlas.logistics_rate_cards WHERE id=$1',
[card],
);
assert.equal(rateCard.rows[0].status, 'draft');
assert.equal(rateCard.rows[0].reviewed_at, null);
});
it('已上架 SKU 可确认本地新价格,保留首次导入身份且不触发平台任务', async () => {
const otherStore = randomUUID();
await db.workspaceTransaction(workspace, async (tx) => {
await tx.query('UPDATE atlas.products SET model=$2 WHERE id=$1', [product, 'LOCAL-RECORD-MODEL']);
await tx.query(
`UPDATE atlas.product_variant_platform_listings SET status='imported',offer_id='LOCAL-RECORD-SKU',platform_product_id='123456',first_imported_at=now() WHERE profile_id=$1 AND product_variant_id=$2`,
[profile, variant],
);
await tx.query(
`INSERT INTO atlas.stores(id,workspace_id,platform_id,name,platform_client_id,currency_code,status,credential_status) VALUES($1,$2,$3,'未来目标店铺',$4,'RUB','active','valid')`,
[otherStore, workspace, platform, randomUUID()],
);
await tx.query('UPDATE atlas.product_platform_profiles SET target_store_id=$2 WHERE id=$1', [profile, otherStore]);
});
const facts = () => db.workspaceTransaction(workspace, async (tx) => ({
listing: (await tx.query(
'SELECT status,store_id,offer_id,platform_product_id,first_imported_at,selling_price::text,list_price::text,pricing_quote_id FROM atlas.product_variant_platform_listings WHERE profile_id=$1 AND product_variant_id=$2',
[profile, variant],
)).rows[0],
publishJobs: (await tx.query('SELECT id FROM atlas.product_publish_jobs WHERE product_id=$1', [product])).rows.length,
events: (await tx.query('SELECT id FROM atlas.outbox_events')).rows.length,
}));
const before = await facts();
const state = await read();
assert.equal(state.variants.find((v) => v.id === variant)?.listing.store_id, store);
const input = quote();
input.pricing.mode = 'fixed_sale';
input.pricing.selling_price = '100';
input.pricing.list_price_discount_rate = '0.5';
const wrongStore = await request('/pricing/quote-previews', 'POST', { ...input, store_id: otherStore });
assert.equal(wrongStore.status, 422);
assert.equal(wrongStore.error.code, 'PRICING_VARIANT_STORE_MISMATCH');
const preview = await request('/pricing/quote-previews', 'POST', input);
assert.equal(preview.status, 201, JSON.stringify(preview));
const payload = draft(state);
payload.profile.target_store_id = otherStore;
const changed = payload.variants.find((v) => v.id === variant)!;
changed.pricing_draft = input.pricing;
changed.pricing_selection = {
preview_id: preview.data.preview_id,
option_id: preview.data.recommended_option_id,
input_hash: preview.data.input_hash,
};
changed.purchase_cost_cny = '29.00';
assert.equal((await save(state, payload)).status, 409);
assert.deepEqual(await facts(), before);
changed.purchase_cost_cny = '28.00';
const key = randomUUID();
const saved = await save(state, payload, key);
assert.equal(saved.status, 200, JSON.stringify(saved));
assert.deepEqual((await save(state, payload, key)).data, saved.data);
const after = await facts();
assert.equal(after.listing.selling_price, '100.0000');
assert.equal(after.listing.list_price, '200.0000');
assert.notEqual(after.listing.pricing_quote_id, before.listing.pricing_quote_id);
for (const field of ['status', 'store_id', 'offer_id', 'platform_product_id', 'first_imported_at'])
assert.deepEqual(after.listing[field], before.listing[field]);
assert.equal(after.publishJobs, before.publishJobs);
assert.equal(after.events, before.events);
const reread = await read();
const listing = reread.variants.find((v) => v.id === variant)!.listing;
assert.equal(listing.status, 'imported');
assert.equal(listing.pricing_result?.selling_price, '100.00');
const ordinary = draft(reread);
ordinary.profile.target_store_id = otherStore;
assert.equal((await save(reread, ordinary)).status, 200);
assert.deepEqual((await facts()).listing, after.listing);
});
});