release: freeze 2026-10-15 validated release baseline
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Canceled after 0s
CI/CD Pipeline / E2E (live stack) (push) Canceled after 0s
CI/CD Pipeline / Backend Build & Test (push) Canceled after 0s
CI/CD Pipeline / Admin Web Build (push) Canceled after 0s
CI/CD Pipeline / Mini Program Compile Check (push) Canceled after 0s
CI/CD Pipeline / Migration Validation (push) Canceled after 0s
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Canceled after 0s
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Canceled after 0s
CI/CD Pipeline / E2E (live stack) (push) Canceled after 0s
CI/CD Pipeline / Backend Build & Test (push) Canceled after 0s
CI/CD Pipeline / Admin Web Build (push) Canceled after 0s
CI/CD Pipeline / Mini Program Compile Check (push) Canceled after 0s
CI/CD Pipeline / Migration Validation (push) Canceled after 0s
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
from pathlib import Path
|
||||
import unittest
|
||||
|
||||
|
||||
ROOT = Path(__file__).parents[2]
|
||||
|
||||
|
||||
class MatchmakerEntryContractTest(unittest.TestCase):
|
||||
def test_user_login_exposes_staff_entry(self):
|
||||
source = (ROOT / "miniapp/pages/login/login.wxml").read_text(encoding="utf-8")
|
||||
script = (ROOT / "miniapp/pages/login/login.ts").read_text(encoding="utf-8")
|
||||
self.assertIn("红娘工作人员入口", source)
|
||||
self.assertIn("onOperatorEntry", source)
|
||||
self.assertIn("ROUTE.OPERATOR.LOGIN", script)
|
||||
|
||||
def test_app_routes_existing_operator_session_to_workbench(self):
|
||||
source = (ROOT / "miniapp/app.js").read_text(encoding="utf-8")
|
||||
self.assertIn("operatorToken", source)
|
||||
self.assertIn("matchmaker-workbench", source)
|
||||
self.assertIn("stores.bootstrap()", source)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,16 @@
|
||||
from pathlib import Path
|
||||
import unittest
|
||||
|
||||
|
||||
ROOT = Path(__file__).parents[2]
|
||||
|
||||
|
||||
class MatchmakerLoginContractTest(unittest.TestCase):
|
||||
def test_login_uses_public_operator_auth_endpoint(self):
|
||||
source = (ROOT / "miniapp/pages/matchmaker-login/matchmaker-login.js").read_text(encoding="utf-8")
|
||||
self.assertIn("/auth/operator/login", source)
|
||||
self.assertNotIn("/operator/auth/login", source)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,35 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
const path = require('node:path');
|
||||
|
||||
(async () => {
|
||||
let page;
|
||||
let status = 'waiting_user';
|
||||
const requests = [];
|
||||
vm.runInNewContext(fs.readFileSync(path.join(__dirname, '../../miniapp/pages/matchmaker-progress/matchmaker-progress.js'), 'utf8'), {
|
||||
Page: definition => { page = definition; },
|
||||
wx: { getStorageSync: () => 'test-token', showToast: () => {} },
|
||||
require: name => name.endsWith('/api') ? {
|
||||
get: async url => {
|
||||
requests.push(url);
|
||||
assert.equal(url, '/matchmaker/services');
|
||||
return [{ id: 'other', status: 'completed' }, { id: 'demo-service', status }];
|
||||
},
|
||||
} : { matchmakerService: { confirmService: async id => {
|
||||
assert.equal(id, 'demo-service');
|
||||
status = 'in_service';
|
||||
} } },
|
||||
});
|
||||
page.setData = values => Object.assign(page.data, values);
|
||||
await page.loadService('demo-service');
|
||||
assert.equal(page.data.service.id, 'demo-service');
|
||||
await page.onConfirmService();
|
||||
assert.equal(page.data.service.status, 'in_service');
|
||||
assert.equal(page.data.confirming, false);
|
||||
assert.equal(requests.length, 2);
|
||||
await page.loadService('missing');
|
||||
assert.equal(page.data.service, null);
|
||||
assert.equal(page.data.loading, false);
|
||||
console.log('PASS: 本人列表定位、确认后刷新及缺失服务处理');
|
||||
})().catch(error => { console.error(error); process.exitCode = 1; });
|
||||
@@ -0,0 +1,30 @@
|
||||
from pathlib import Path
|
||||
import json
|
||||
import unittest
|
||||
|
||||
|
||||
ROOT = Path(__file__).parents[2]
|
||||
|
||||
|
||||
class MatchmakerRecommendationContractTest(unittest.TestCase):
|
||||
def test_recommendation_page_is_registered_and_user_decision_only(self):
|
||||
app = json.loads((ROOT / "miniapp/app.json").read_text(encoding="utf-8"))
|
||||
self.assertIn("pages/matchmaker-recommendations/matchmaker-recommendations", app["pages"])
|
||||
source = (ROOT / "miniapp/pages/matchmaker-recommendations/matchmaker-recommendations.js").read_text(encoding="utf-8")
|
||||
self.assertIn("fetchRecommendations", source)
|
||||
self.assertIn("matchStore.like", source)
|
||||
self.assertIn("matchStore.pass", source)
|
||||
self.assertNotIn("operatorToken", source)
|
||||
|
||||
def test_matchmaker_service_routes_to_dedicated_recommendation_page(self):
|
||||
source = (ROOT / "miniapp/pages/matchmaker/matchmaker.ts").read_text(encoding="utf-8")
|
||||
self.assertIn("MATCHMAKER_RECOMMENDATIONS", source)
|
||||
self.assertNotIn("ROUTE.MAIN.FATE_CENTER, { id: serviceId }", source)
|
||||
|
||||
def test_recommendation_store_accepts_array_api_response(self):
|
||||
source = (ROOT / "miniapp/stores/matchmakerStore.js").read_text(encoding="utf-8")
|
||||
self.assertIn("Array.isArray(result)", source)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,29 @@
|
||||
from pathlib import Path
|
||||
import unittest
|
||||
|
||||
|
||||
ROOT = Path(__file__).parents[2]
|
||||
|
||||
|
||||
class MatchmakerReferralTaskContractTest(unittest.TestCase):
|
||||
def test_workbench_loads_operator_referral_history(self):
|
||||
source = (ROOT / "miniapp/pages/matchmaker-workbench/matchmaker-workbench.js").read_text(encoding="utf-8")
|
||||
wxml = (ROOT / "miniapp/pages/matchmaker-workbench/matchmaker-workbench.wxml").read_text(encoding="utf-8")
|
||||
self.assertIn("/referrals/operator/history", source)
|
||||
self.assertIn("referralTasks", source)
|
||||
self.assertIn("referralTasks.length", wxml)
|
||||
self.assertIn("{{item.statusLabel}}", wxml)
|
||||
self.assertIn("{{item.targetLabel}}", wxml)
|
||||
self.assertIn("查看详情", wxml)
|
||||
self.assertIn("发起转介", wxml)
|
||||
self.assertIn("/referrals/activity", source)
|
||||
self.assertIn("/referrals/service-station", source)
|
||||
self.assertIn("/operator/matchmaker/recommendations", source)
|
||||
self.assertIn("推荐进展", wxml)
|
||||
self.assertNotIn("matchStore.like", source)
|
||||
self.assertNotIn("matchStore.pass", source)
|
||||
self.assertNotIn("emergencyContactPhone", source)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,17 @@
|
||||
from pathlib import Path
|
||||
import unittest
|
||||
|
||||
|
||||
ROOT = Path(__file__).parents[2]
|
||||
|
||||
|
||||
class MatchmakerServiceCardContractTest(unittest.TestCase):
|
||||
def test_store_normalizes_api_entity_fields_for_card_navigation(self):
|
||||
source = (ROOT / "miniapp/stores/matchmakerStore.js").read_text(encoding="utf-8")
|
||||
self.assertIn("serviceId: service.serviceId || service.id", source)
|
||||
self.assertIn("requirement: service.requirement || service.description || ''", source)
|
||||
self.assertIn(".map((item) => this.normalizeService(item))", source)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,26 @@
|
||||
from pathlib import Path
|
||||
import unittest
|
||||
|
||||
|
||||
ROOT = Path(__file__).parents[2]
|
||||
|
||||
|
||||
class MatchmakerServiceConfirmationContractTest(unittest.TestCase):
|
||||
def test_backend_exposes_user_confirmation_and_writes_in_service(self):
|
||||
controller = (ROOT / "backend/src/main/java/com/danyang/match/platform/matchmaker/controller/MatchmakerController.java").read_text(encoding="utf-8")
|
||||
service = (ROOT / "backend/src/main/java/com/danyang/match/platform/matchmaker/service/MatchmakerService.java").read_text(encoding="utf-8")
|
||||
mapper = (ROOT / "backend/src/main/java/com/danyang/match/platform/matchmaker/mapper/MatchmakerServiceMapper.java").read_text(encoding="utf-8")
|
||||
self.assertIn('@PostMapping("/services/{id}/confirm")', controller)
|
||||
self.assertIn("confirmService(id, principal.id())", controller)
|
||||
self.assertIn("confirmWaitingForUser", mapper)
|
||||
|
||||
def test_progress_page_only_offers_confirmation_for_waiting_user(self):
|
||||
source = (ROOT / "miniapp/pages/matchmaker-progress/matchmaker-progress.js").read_text(encoding="utf-8")
|
||||
wxml = (ROOT / "miniapp/pages/matchmaker-progress/matchmaker-progress.wxml").read_text(encoding="utf-8")
|
||||
self.assertIn("confirmService", source)
|
||||
self.assertIn("this.data.service.status !== 'waiting_user'", source)
|
||||
self.assertIn("bindtap=\"onConfirmService\"", wxml)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,30 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
const source = fs.readFileSync('miniapp/app.js', 'utf8');
|
||||
|
||||
function launch(token, path) {
|
||||
const calls = [];
|
||||
let app;
|
||||
vm.runInNewContext(source, {
|
||||
require: () => ({
|
||||
bootstrap: () => calls.push('user-bootstrap'),
|
||||
appStore: { init: () => { calls.push('config'); return Promise.resolve(); } },
|
||||
}),
|
||||
App: (definition) => { app = definition; },
|
||||
wx: {
|
||||
getStorageSync: () => token,
|
||||
reLaunch: ({ url }) => calls.push(url),
|
||||
},
|
||||
});
|
||||
app.onLaunch({ path });
|
||||
return calls;
|
||||
}
|
||||
|
||||
assert.deepEqual(launch('', 'pages/index/index'), ['user-bootstrap']);
|
||||
assert.deepEqual(launch('staff-session', 'pages/index/index'), ['config', '/pages/matchmaker-workbench/matchmaker-workbench']);
|
||||
for (const path of ['login', 'workbench', 'service-detail']) {
|
||||
assert.deepEqual(launch('', `pages/matchmaker-${path}/matchmaker-${path}`), ['config']);
|
||||
}
|
||||
assert.deepEqual(launch('', 'pages/matchmaker-progress/matchmaker-progress'), ['user-bootstrap']);
|
||||
console.log('PASS: user startup, staff session, staff deep links and user service route');
|
||||
@@ -0,0 +1,46 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
const path = require('node:path');
|
||||
const root = path.resolve(__dirname, '../..');
|
||||
function load(file, api, service) {
|
||||
let page;
|
||||
vm.runInNewContext(fs.readFileSync(path.join(root, file), 'utf8'), {
|
||||
Page: value => { page = value; },
|
||||
wx: { getStorageSync: () => 'test', showToast() {}, navigateBack() {} },
|
||||
require: name => name.endsWith('/api') ? api : service,
|
||||
});
|
||||
page.setData = data => Object.assign(page.data, data);
|
||||
return page;
|
||||
}
|
||||
(async () => {
|
||||
const id = '00000000-0000-0000-0000-000000000001';
|
||||
const calls = [];
|
||||
const service = { referralService: {
|
||||
getMyReferralHistory: async () => [{ id, type: 'activity', activityId: id,
|
||||
activityDisplayId: 'HD20261014001', referralDisplayId: 'ZF20261014001', status: 'pending' }],
|
||||
confirmReferral: async (value, type) => { calls.push({ value, type }); },
|
||||
} };
|
||||
const referral = load('miniapp/pages/referral-confirmation/referral-confirmation.js', {}, service);
|
||||
await referral.load();
|
||||
assert.equal(referral.data.referrals[0].detail, 'HD20261014001');
|
||||
assert.equal(referral.data.referrals[0].referralDisplayId, 'ZF20261014001');
|
||||
await referral.confirm({ currentTarget: { dataset: { index: 0 } } });
|
||||
assert.equal(calls[0].value, id);
|
||||
assert.equal(calls[0].type, 'activity');
|
||||
service.referralService.getMyReferralHistory = async () => [{ id, type: 'activity', activityId: id }];
|
||||
await referral.load();
|
||||
assert.equal(referral.data.referrals[0].detail, '公益活动');
|
||||
const urls = [];
|
||||
const detail = load('miniapp/pages/matchmaker-service-detail/matchmaker-service-detail.js', {
|
||||
get: async url => { urls.push(url); return { serviceId: id, serviceDisplayId: 'FW20261014001', userDisplayNumber: 'YH20261014002', followups: [] }; },
|
||||
}, {});
|
||||
detail.data.serviceId = id;
|
||||
await detail.loadDetail();
|
||||
assert.equal(urls[0], `/operator/matchmaker/services/${id}`);
|
||||
assert.equal(detail.data.detail.serviceDisplayId, 'FW20261014001');
|
||||
const template = fs.readFileSync(path.join(root, 'miniapp/pages/matchmaker-service-detail/matchmaker-service-detail.wxml'), 'utf8');
|
||||
assert(template.includes('detail.serviceDisplayId'));
|
||||
assert(!template.includes('{{detail.serviceId}}'));
|
||||
console.log('PASS: 小程序真实页面脚本显示业务编号、缺号不回退 UUID、确认和详情请求保留内部 UUID。');
|
||||
})().catch(error => { console.error(error); process.exitCode = 1; });
|
||||
@@ -0,0 +1,31 @@
|
||||
from pathlib import Path
|
||||
import json
|
||||
import unittest
|
||||
|
||||
|
||||
ROOT = Path(__file__).parents[2]
|
||||
|
||||
|
||||
class ReferralPageContractTest(unittest.TestCase):
|
||||
def test_referral_confirmation_page_is_registered_and_user_only(self):
|
||||
app = json.loads((ROOT / "miniapp/app.json").read_text(encoding="utf-8"))
|
||||
self.assertIn("pages/referral-confirmation/referral-confirmation", app["pages"])
|
||||
page = ROOT / "miniapp/pages/referral-confirmation/referral-confirmation.js"
|
||||
source = page.read_text(encoding="utf-8")
|
||||
self.assertIn("getMyReferralHistory", source)
|
||||
self.assertIn("confirmReferral", source)
|
||||
self.assertIn("load()", source)
|
||||
self.assertNotIn("operatorToken", source)
|
||||
service_source = (ROOT / "miniapp/services/referralService.js").read_text(encoding="utf-8")
|
||||
self.assertIn("referral-confirm:${targetId}:${targetType}", service_source)
|
||||
|
||||
def test_referral_page_does_not_offer_operator_actions_or_sensitive_fields(self):
|
||||
source = (ROOT / "miniapp/pages/referral-confirmation/referral-confirmation.wxml").read_text(encoding="utf-8")
|
||||
self.assertNotIn("代用户", source)
|
||||
self.assertNotIn("手机号", source)
|
||||
self.assertNotIn("身份证", source)
|
||||
self.assertIn("确认转介", source)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user