chore(core,board,ecommerce,page,gdpr): 7.0.6 공개 릴리즈 누출 검사 차단 요인 제거
prepare-release.sh 의 파일 본문 누출 검사가 error 10건으로 릴리즈를 차단하고 있었다. 공개 저장소는 를 제외하므로, 배포되는 파일이 그 경로나 를 참조하면 공개본에서 깨진 참조가 된다. - 시나리오 매니페스트 4건: test_files 의 내부 하네스 테스트 경로 제거 - Playwright spec 3건: 룰 파일 경로/@see 포인터를 자기완결 서술로 교체 (@see 2건은 이미 stale — 가리키던 사례 번호가 실제와 달랐다) - 소스/테스트 주석 3건: 파일명 노출 제거 내부 역할 호칭이 공개본에 영구 노출되는 warn 23건 중 21건도 함께 중립 표현으로 정정했다. API 표면 파일 2건은 주석만 고쳐도 문서 커버리지 게이트에 영구 면제가 필요해져 보류한다 — warn 은 릴리즈를 차단하지 않는다. 부수로 드러난 기존 결함 둘을 같은 세션에서 처리: - gdpr Playwright config 에 locale 고정 누락 — 첫 로드가 en-US 로 나가 화면이 영어로 굳으면 한국어 문구를 단언하는 spec 이 전부 실패한다 - 언어팩 시나리오의 정적 검사 계층 전용 효과 2건을 effects 에서 rules_layer_coverage 로 이관 — 런타임 테스트로 성립하지 않는 항목이라 그 칸이 정합 위치다 검증: 누출 검사 error 0, audit --working 위반 0(룰 192), 시나리오 매니페스트 8건 errors 0, gdpr Vitest 148 green, PHP lint/JSON/YAML 정상. 버전 정합은 코어 working 7.0.6 / 공개 latest 7.0.5 = new-bump 로 릴리즈 가능 상태.
This commit is contained in:
+1
-1
@@ -12,7 +12,7 @@
|
||||
* 단위/렌더링:
|
||||
* - templates/_bundled/sirsoft-basic/src/__tests__/layouts/board-post-navigation-list-return.test.tsx
|
||||
* 가 실 `_navigation.json` 의 액션 params 로 mergeQuery 왕복을 고정
|
||||
* - .claude/scripts/audit/rules/layout-list-context-navigate-merge-query.cjs 가 전 저장소 재발 차단
|
||||
* - 저장소 전역 정적 검사가 목록 클러스터 내 navigate 의 mergeQuery 누락을 재발 차단
|
||||
* 이 spec 은 브라우저 수준(실제 URL + 목록 API 요청 파라미터)을 담당한다.
|
||||
*
|
||||
* @scenario board-list-context-round-trip
|
||||
|
||||
@@ -35,7 +35,6 @@ test_files:
|
||||
- modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/admin-list-context-round-trip.test.ts
|
||||
- modules/_bundled/sirsoft-page/resources/js/__tests__/layouts/admin-list-context-round-trip.test.ts
|
||||
- templates/_bundled/sirsoft-admin_basic/src/__tests__/layouts/admin-list-context-round-trip.test.ts
|
||||
- .claude/scripts/audit/__tests__/layout-list-context-navigate-merge-query.test.cjs
|
||||
|
||||
validation:
|
||||
note: |
|
||||
|
||||
@@ -178,7 +178,7 @@ class CouponIssueRepository implements CouponIssueRepositoryInterface
|
||||
// coupon 관계를 직렬화하지 않지만, 이 목록은 훅
|
||||
// `sirsoft-ecommerce.user_coupon.filter_list_result` 로 확장에 그대로 전달된다.
|
||||
// 제3자 확장이 쿠폰 원본 정의(쿠폰명·할인율·사용조건)를 읽고 있을 수 있어 현행 유지
|
||||
// (PO 결정 2026-08-04). 번들 템플릿에는 아직 대응 마이페이지 화면이 없다.
|
||||
// (프로젝트 결정 2026-08-04). 번들 템플릿에는 아직 대응 마이페이지 화면이 없다.
|
||||
relations: ['coupon'],
|
||||
resultCap: PaginationLimits::resultCap('ecommerce.coupon_issues'),
|
||||
);
|
||||
|
||||
@@ -705,7 +705,7 @@ class CashReceiptService
|
||||
/**
|
||||
* 프로바이더 원응답에서 민감 키를 마스킹합니다.
|
||||
*
|
||||
* PO 결정: 전체 응답을 보관하되 알려진 민감 키를 재귀적으로 가린다.
|
||||
* 프로젝트 결정: 전체 응답을 보관하되 알려진 민감 키를 재귀적으로 가린다.
|
||||
*
|
||||
* @param mixed $response 원응답
|
||||
* @return array<string, mixed>|null 마스킹된 응답 (배열이 아니면 null)
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Modules\Sirsoft\Ecommerce\Support;
|
||||
* 를 이 계약이 도입된 `>=1.1.0` 이상으로 선언해야 한다.
|
||||
*
|
||||
* 시그니처를 바꾸거나 제거할 때는 위 소비 확장을 전수 확인하고 각 확장의 최소 버전
|
||||
* 제약을 함께 올린다 (CLAUDE.md "버전 동기화 의무" ②).
|
||||
* 제약(`dependencies.modules.sirsoft-ecommerce`)을 함께 올린다.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 이커머스 모듈 Playwright E2E 설정.
|
||||
*
|
||||
* 코어 `playwright.config.ts` 와 동일한 base URL 해석 우선순위를 따른다 — 모듈도 활성 호스트가
|
||||
* 가변(개발자/CI/PO 환경별로 다른 도메인)이므로 하드코딩 회피.
|
||||
* 가변(개발자/CI/운영 환경별로 다른 도메인)이므로 하드코딩 회피.
|
||||
*
|
||||
* Base URL 해석:
|
||||
* 1. PLAYWRIGHT_BASE_URL 환경변수 (CI/명시적 오버라이드)
|
||||
|
||||
+2
-1
@@ -28,7 +28,8 @@
|
||||
* 부작용: ②③ 은 실제 주문을 생성한다(결제 대기 상태). 무통장입금은 외부 PG 를 거치지 않으므로
|
||||
* 샌드박스 의존이 없고, 생성된 주문은 설정된 기한이 지나면 미입금 자동취소 대상이 된다.
|
||||
*
|
||||
* @see .claude/docs/frontend/troubleshooting-backend.md 사례 21
|
||||
* 회귀 배경: 입금기한 일수 설정이 문자열로 영속되면 기한 산정이 Carbon 의 strict 타입 경계에서
|
||||
* TypeError 를 던져 해당 주문 요청이 전부 500 이 되던 결함이 있었다.
|
||||
*/
|
||||
import { test, expect, authenticatePage } from '../../fixtures/ecommerce-auth';
|
||||
import type { Page } from '@playwright/test';
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ notes: |
|
||||
(`OrderCalculationService`)가 그 관계를 직접 읽고, 미로드 시 항목마다 재조회하므로 좁히면
|
||||
페이로드 대신 쿼리가 늘어난다. 게다가 계산기가 `is_active` 를 거르지 않아 로드 경로에 따라
|
||||
배송비가 달라질 수 있다 — 계산 경로를 함께 손대야 안전한 축이라 본 이슈에서 다루지 않는다
|
||||
(PO 결정, 2026-08-05).
|
||||
(프로젝트 결정, 2026-08-05).
|
||||
|
||||
E2E 미해당: 응답 형태가 불변이라 브라우저 경로에 새 분기가 없다. 소비 사실 고정은
|
||||
레이아웃 원문 검사(`cartWishlistImageConsumers.test.ts`)가 담당한다.
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@ description: |
|
||||
리터럴 객체다. 슬롯에 주입된 모듈 폼이 _local 에 값을 써도 그 body 에 실릴 방법이 없다 —
|
||||
결제 버튼에 id 가 없어 overlay 로 타겟할 수 없고, inject_props 는 컴포넌트 props 전용이라
|
||||
actions[].params.body 에 도달하지 못한다. 그대로 두면 구매자가 신청해도 주문에 아무 기록이 남지 않는다.
|
||||
→ PO 결정: body 를 통짜 표현식으로 바꾸고 확장 병합 칸(...(_local.checkoutExtraPayload ?? {})) 한 개를 연다.
|
||||
→ 프로젝트 결정: body 를 통짜 표현식으로 바꾸고 확장 병합 칸(...(_local.checkoutExtraPayload ?? {})) 한 개를 연다.
|
||||
템플릿은 현금영수증을 알지 않고, 모듈은 그 칸에만 쓴다. 확장 미주입 시 payload 는 전환 전과 동일하다.
|
||||
|
||||
발견한 사전 결함(본 세션에서 수정):
|
||||
@@ -29,7 +29,7 @@ description: |
|
||||
· 발급 버튼과 재발급 버튼의 노출 조건이 배타적이지 않아 FAILED 상태에서 둘 다 떴다.
|
||||
· 이력 표가 원시값을 노출했다 — 취소 행에 "발급 완료", 실패 행 일시 "-"(issued_at null), issue/FAILED.
|
||||
|
||||
환불계좌는 PG·현금영수증 프로바이더와 무관한 순수 주문 정보이므로 템플릿이 직접 소유한다(PO 결정).
|
||||
환불계좌는 PG·현금영수증 프로바이더와 무관한 순수 주문 정보이므로 템플릿이 직접 소유한다(프로젝트 결정).
|
||||
취소 시점 갱신은 레이아웃이 아니라 cancelOrderHandlers.ts 가 수행한다 — 취소 POST body 를
|
||||
그 핸들러가 조립하기 때문이다.
|
||||
|
||||
@@ -215,7 +215,7 @@ effects:
|
||||
- portable_override_applies_below_1024_on_render # 실렌더: 세로 스택 전환 (F5)
|
||||
- portable_override_absent_at_1024_on_render # 실렌더: 1024px 부터 base 복귀
|
||||
|
||||
# 데이터 위생 (감사 지적 C8, PO 결정 2026-07-10)
|
||||
# 데이터 위생 (감사 지적 C8, 프로젝트 결정 2026-07-10)
|
||||
- cash_receipt_request_not_persisted_for_non_dbank # 발급 불가 주문에 암호문 미보관
|
||||
|
||||
# 자진발급 설정 안내 (감사 지적 W-5)
|
||||
|
||||
-1
@@ -22,7 +22,6 @@ effects:
|
||||
test_files:
|
||||
- modules/_bundled/sirsoft-ecommerce/tests/Playwright/specs/admin/order-admin-list-context-round-trip.spec.ts
|
||||
- modules/_bundled/sirsoft-ecommerce/resources/js/__tests__/layouts/admin-list-context-round-trip.test.ts
|
||||
- .claude/scripts/audit/__tests__/layout-list-context-navigate-merge-query.test.cjs
|
||||
|
||||
validation:
|
||||
note: |
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ feature: 리뷰 목록의 첨부 이미지 계약 고정 (#518 / 공개 #76)
|
||||
description: |
|
||||
#518 원안은 리뷰 목록에서 `images` 를 빼고 대표 1장 + `image_count` 로 줄이려 했다.
|
||||
소비처를 재실측한 결과 그 판단이 빗나갔다 — 관리자·공개 두 목록 모두 첨부 이미지를
|
||||
실제로 그린다. 착수하지 않는다 (PO 결정, 2026-08-05).
|
||||
실제로 그린다. 착수하지 않는다 (프로젝트 결정, 2026-08-05).
|
||||
|
||||
이 매니페스트는 "줄였다" 를 기록하는 것이 아니라 **줄일 수 없다는 사실을 고정**한다.
|
||||
제거 시 조용히 깨지기 때문이다: 썸네일이 `(review.images ?? []).length > 0` 가드 뒤에
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 페이지 모듈 Playwright E2E 설정.
|
||||
*
|
||||
* 코어 `playwright.config.ts` 및 이커머스 모듈 설정과 동일한 base URL 해석 우선순위를 따른다 —
|
||||
* 모듈도 활성 호스트가 가변(개발자/CI/PO 환경별로 다른 도메인)이므로 하드코딩 회피.
|
||||
* 모듈도 활성 호스트가 가변(개발자/CI/운영 환경별로 다른 도메인)이므로 하드코딩 회피.
|
||||
*
|
||||
* Base URL 해석:
|
||||
* 1. PLAYWRIGHT_BASE_URL 환경변수 (CI/명시적 오버라이드)
|
||||
|
||||
@@ -19,7 +19,6 @@ effects:
|
||||
test_files:
|
||||
- modules/_bundled/sirsoft-page/tests/Playwright/specs/admin/page-admin-list-context-round-trip.spec.ts
|
||||
- modules/_bundled/sirsoft-page/resources/js/__tests__/layouts/admin-list-context-round-trip.test.ts
|
||||
- .claude/scripts/audit/__tests__/layout-list-context-navigate-merge-query.test.cjs
|
||||
|
||||
validation:
|
||||
note: |
|
||||
|
||||
@@ -534,7 +534,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"comment": "항목 리스트 (이슈 #430 재설계) — 상태 배지 + 동의/철회 버튼 방식. 필수=상태 배지 '항상 적용' + 잠금 버튼(액션 없음), 선택=상태 배지(동의함/거부함/미설정) + 동의·철회 버튼(확인 모달 경유). 동의일 컬럼은 사용자 화면에서 제거(동의한 항목만 라벨 아래 날짜 표시, 감사 로그 전체는 관리자 화면에만 존재). 초기 설계였던 on/off 스위치는 user 템플릿 스타일 회귀 + PO 지시로 배지+버튼 방식으로 대체됨(2026-07-21).",
|
||||
"comment": "항목 리스트 (이슈 #430 재설계) — 상태 배지 + 동의/철회 버튼 방식. 필수=상태 배지 '항상 적용' + 잠금 버튼(액션 없음), 선택=상태 배지(동의함/거부함/미설정) + 동의·철회 버튼(확인 모달 경유). 동의일 컬럼은 사용자 화면에서 제거(동의한 항목만 라벨 아래 날짜 표시, 감사 로그 전체는 관리자 화면에만 존재). 초기 설계였던 on/off 스위치는 user 템플릿 스타일 회귀 + 프로젝트 지시로 배지+버튼 방식으로 대체됨(2026-07-21).",
|
||||
"type": "basic",
|
||||
"name": "Div",
|
||||
"if": "{{(gdprMeConsents?.data?.consents ?? []).length > 0}}",
|
||||
|
||||
+1
-1
@@ -564,7 +564,7 @@ describe('admin/plugin_settings.json — 3 카드 (operator / cookie_banner / au
|
||||
|
||||
/**
|
||||
* sequence handler 구조 회귀 가드 — actions 배열이 params 안에 잘못 중첩되어
|
||||
* (CLAUDE.md 절대 금지 패턴) 있었던 결함. 엔진(ActionDispatcher.handleSequence)이
|
||||
* (레이아웃 JSON 금지 패턴) 있었던 결함. 엔진(ActionDispatcher.handleSequence)이
|
||||
* 두 위치를 모두 인식하는 fallback을 갖고 있어 동작 자체는 정상이었지만, 정석
|
||||
* 위치(actions가 액션 객체의 top-level)로 바로잡는다.
|
||||
*/
|
||||
|
||||
+2
-2
@@ -312,7 +312,7 @@ describe('extensions/mypage_privacy_tab.json — 마이페이지 GDPR 동의 매
|
||||
* @effects mypage_status_date_line_consented_only
|
||||
*/
|
||||
it('이슈 #430/#509 — 동의·철회 이력이 있는 항목만 날짜 줄 노출 (서버가 status_at_formatted 제공)', () => {
|
||||
// PO 결정(2026-07-20) + 이슈 #509 16번 갱신: 날짜는 "동의한 항목" + "철회 이력이 있는 항목".
|
||||
// 프로젝트 결정(2026-07-20) + 이슈 #509 16번 갱신: 날짜는 "동의한 항목" + "철회 이력이 있는 항목".
|
||||
// 거부·신규 미설정은 서버가 status_at_formatted 를 null 로 내려주므로 날짜 줄이 렌더되지 않는다.
|
||||
// 프론트는 status_label && status_at_formatted 가 둘 다 있을 때만 날짜 줄을 그리며, 값 자체는
|
||||
// 서버가 상태별(동의일/철회일)로 계산해 내려주므로 프론트 조건식은 상태 무관 동일하게 유지된다.
|
||||
@@ -328,7 +328,7 @@ describe('extensions/mypage_privacy_tab.json — 마이페이지 GDPR 동의 매
|
||||
* @effects mypage_status_date_line_revoked_color_distinction
|
||||
*/
|
||||
it('이슈 #509 16번 — 동의·철회 날짜 줄을 서로 다른 색으로 균형있게 강조 (동의=초록/철회=빨강, 배지·관리자 이력 화면과 동일 톤)', () => {
|
||||
// 철회만 강조되고 동의는 밋밋해 보인다는 PO 피드백 반영 — 동의도 철회와 대등하게 강조한다.
|
||||
// 철회만 강조되고 동의는 밋밋해 보인다는 사용성 피드백 반영 — 동의도 철회와 대등하게 강조한다.
|
||||
// status === 'revoked' 면 text-red-600/dark:text-red-400, 그 외(동의)는 text-green-600/dark:text-green-400.
|
||||
const text = serializeForSearch(section);
|
||||
expect(text).toContain("consent?.status === 'revoked'");
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* GDPR 플러그인 Playwright E2E 설정.
|
||||
*
|
||||
* 코어 `playwright.config.ts` 와 동일한 base URL 해석 우선순위를 따른다 — 플러그인도 활성 호스트가
|
||||
* 가변(개발자/CI/PO 환경별로 다른 도메인)이므로 하드코딩 회피.
|
||||
* 가변(개발자/CI/운영 환경별로 다른 도메인)이므로 하드코딩 회피.
|
||||
*
|
||||
* Base URL 해석:
|
||||
* 1. PLAYWRIGHT_BASE_URL 환경변수 (CI/명시적 오버라이드)
|
||||
@@ -76,6 +76,12 @@ export default defineConfig({
|
||||
],
|
||||
use: {
|
||||
baseURL: resolveBaseUrl(),
|
||||
// spec 이 한국어 화면 문구를 단언하므로 로케일을 고정한다.
|
||||
// 로케일 우선순위는 localStorage g7_locale → 서버 응답값 → 'ko' 이고, 서버값은 미인증
|
||||
// 요청에서 Accept-Language 로 결정된다(SetLocale 미들웨어). Playwright 의 locale 옵션이
|
||||
// 그 헤더를 만들므로, 지정하지 않으면 첫 페이지 로드가 en-US 로 나가 화면이 영어로 렌더되고
|
||||
// 엔진이 그 값을 localStorage 에 저장해 이후 인증해도 세션 전체가 영어로 고정된다.
|
||||
locale: 'ko-KR',
|
||||
trace: 'retain-on-failure',
|
||||
screenshot: 'only-on-failure',
|
||||
ignoreHTTPSErrors: true,
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* E2E: 관리자 환경설정 > 쿠키 배너 > 정책 버전 > 보기 — 본문 페이지 링크 (#509 체크리스트 12번)
|
||||
* + 관리자 환경설정 카드 제목 색상 회귀 가드 (PO 스크린샷 발견)
|
||||
* + 관리자 환경설정 카드 제목 색상 회귀 가드 (브라우저 스크린샷으로 발견)
|
||||
* + 정책 버전 이력 토글 sequence handler 회귀 가드
|
||||
*
|
||||
* @scenario admin_gdpr_policy_version_snapshot_open_policy_page_link
|
||||
@@ -13,11 +13,11 @@
|
||||
* 모달의 본문 페이지 링크 href 를 검증할 수 있다.
|
||||
*
|
||||
* 또한 카드 제목(H3)이 에러/경고 전용 시맨틱(text-error-strong, red 계열)을 오용해
|
||||
* "운영 주체" 등 일반 카드 제목이 빨간색으로 표시되던 결함을 PO 가 스크린샷으로 발견해
|
||||
* "운영 주체" 등 일반 카드 제목이 빨간색으로 표시되던 결함을 브라우저 스크린샷으로 발견해
|
||||
* section-heading-md(중립 톤)로 수정 — 실제 렌더링된 색상을 브라우저에서 검증한다.
|
||||
*
|
||||
* 정책 버전 이력 토글 버튼의 sequence handler 가 actions 를 params 안에 잘못 중첩하고
|
||||
* 있던 결함(CLAUDE.md 절대 금지 패턴)도 함께 발견 — 엔진이 두 위치 모두 인식하는
|
||||
* 있던 결함(레이아웃 JSON 금지 패턴)도 함께 발견 — 엔진이 두 위치 모두 인식하는
|
||||
* fallback을 갖고 있어 동작 자체는 정상이었으나 정석 위치(top-level)로 바로잡았다.
|
||||
* 실제 클릭 시 이력 표가 펼쳐지는지(=sequence 의 setState 가 실행됐는지)를 검증한다.
|
||||
*
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
*
|
||||
* 배경: `_global.gdprBannerDismissedFor` 가 "닫힘 여부"(boolean)가 아니라 "누가 닫았는지"
|
||||
* (사용자 식별자)를 저장하도록 수정되기 전에는, 게스트로 배너를 닫은 뒤 동의 이력이 없는
|
||||
* 다른 계정으로 로그인해도 배너가 계속 숨겨져 있는 결함이 있었다 (PO 가 스크린샷으로 재현·지적).
|
||||
* 다른 계정으로 로그인해도 배너가 계속 숨겨져 있는 결함이 있었다 (브라우저 스크린샷으로 재현·지적).
|
||||
* 현재는 cookie_banner.json 의 노출 조건이 `_global.gdprBannerDismissedFor !== (사용자 식별자)`
|
||||
* 로 비교하므로, 로그인으로 사용자가 바뀌면(게스트 uuid → 회원 uuid) 다시 노출되어야 한다.
|
||||
*
|
||||
|
||||
+1
-1
@@ -66,7 +66,7 @@ class GdprUserConsentResourceTest extends PluginTestCase
|
||||
|
||||
public function test_rejected_optional_maps_to_rejected_status_with_no_date_label(): void
|
||||
{
|
||||
// PO 결정(2026-07-20) — 날짜/라벨은 "동의한 항목만" 노출. 거부는 상태만 rejected 로 내려주고
|
||||
// 프로젝트 결정(2026-07-20) — 날짜/라벨은 "동의한 항목만" 노출. 거부는 상태만 rejected 로 내려주고
|
||||
// status_label·status_at_formatted 는 null (프론트가 날짜 줄을 렌더하지 않음).
|
||||
$consent = new GdprUserConsent([
|
||||
'consent_key' => 'cookie_analytics',
|
||||
|
||||
@@ -701,13 +701,13 @@ class LanguagePackControllerTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* 번들 설치도 외부 소스와 동일하게 활성화 권한 게이트를 적용한다 (PO 결정, 2026-08-04).
|
||||
* 번들 설치도 외부 소스와 동일하게 활성화 권한 게이트를 적용한다 (프로젝트 결정, 2026-08-04).
|
||||
*
|
||||
* 설치(install)와 활성화(manage)를 별도 권한으로 두는 정책을 네 경로 전부에 같은 강도로
|
||||
* 적용한다 — 어느 경로로 들어오느냐가 권한 경계를 바꾸면 그 경로가 우회로가 된다.
|
||||
*
|
||||
* 검토 단계에서는 "번들은 저장소 동봉 신뢰 자산이라 임의 PHP 반입 경로가 아니다" 를 근거로
|
||||
* 면제했으나, PO 가 네 경로 일관 적용을 선택했다. 그 결과 설치 권한만 가진 운영자가 활성
|
||||
* 면제했으나, 네 경로에 일관 적용하는 것으로 결정했다. 그 결과 설치 권한만 가진 운영자가 활성
|
||||
* 팩을 재설치하면 그 팩이 `installed` 로 내려가고, 다시 켜려면 관리 권한이 필요하다.
|
||||
*
|
||||
* @scenario vector=install_from_bundled, actor_permission=install_only
|
||||
|
||||
@@ -38,7 +38,7 @@ class DevtoolsRouteCacheTest extends TestCase
|
||||
* 덤프 위임이 실제로 수행됐는지 확인할 때 쓰는 섹션.
|
||||
*
|
||||
* 하네스 자식 프로세스는 실 `storage/debug-dump` 에 쓴다 (경로가 `storage_path()` 고정).
|
||||
* PO 가 브라우저에서 받아 둔 덤프를 테스트가 덮어쓰지 않도록 이 파일만 스냅샷·복원한다.
|
||||
* 개발자가 브라우저에서 받아 둔 덤프를 테스트가 덮어쓰지 않도록 이 파일만 스냅샷·복원한다.
|
||||
*/
|
||||
private const PROBE_SECTION = 'logs';
|
||||
|
||||
@@ -83,7 +83,7 @@ class DevtoolsRouteCacheTest extends TestCase
|
||||
self::removeDirectory(dirname(__DIR__, 3).'/'.self::$cacheRelDir);
|
||||
}
|
||||
|
||||
// PO 의 덤프 데이터를 원상 복구 (자식 프로세스가 실 storage 에 썼다).
|
||||
// 개발자의 덤프 데이터를 원상 복구 (자식 프로세스가 실 storage 에 썼다).
|
||||
$probePath = dirname(__DIR__, 3).'/storage/debug-dump/'.self::PROBE_FILE;
|
||||
if (self::$probeExisted && self::$probeBackup !== null) {
|
||||
file_put_contents($probePath, self::$probeBackup);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* 풀린 상태로 `/admin/` 에 접근하면 대시보드 → 로그인 리다이렉트 흐름 대신 404 가
|
||||
* 노출됐다. 수정: `Router.match()` 진입 시 끝 슬래시를 정규화한다.
|
||||
*
|
||||
* 단위 테스트(Router.test.ts)가 매칭 로직을 잠그지만, PO 가 실제로 브라우저에서
|
||||
* 단위 테스트(Router.test.ts)가 매칭 로직을 잠그지만, 실제 브라우저에서
|
||||
* 재현한 증상이므로(단위 green + 브라우저 깨짐 방지) 브라우저 레벨로도 잠근다.
|
||||
*
|
||||
* @scenario admin_trailing_slash_unauthenticated + admin_no_trailing_slash_unauthenticated
|
||||
|
||||
@@ -32,7 +32,8 @@
|
||||
* 단위 테스트는 이 결함을 잡지 못한다 — React 렌더 사이클과 복수 루트 렌더러의 effect 실행
|
||||
* 순서를 모사하지 못하기 때문이다.
|
||||
*
|
||||
* @see .claude/docs/frontend/troubleshooting-state-global.md 사례 5
|
||||
* 회귀 배경: 탭 왕복 후 initLocal 동기화가 편집 중이던 입력칸의 값을 되돌려, 먼저 편집한
|
||||
* 입력칸에만 옛 값이 남던 결함 (engine-v1.54.7 에서 수정).
|
||||
*/
|
||||
import { test, expect, issueToken, issueScopedToken, authenticatePage } from '../../fixtures/auth';
|
||||
import type { Page } from '@playwright/test';
|
||||
|
||||
@@ -79,7 +79,7 @@ effects:
|
||||
|
||||
# E2E axis 없음: 판정은 전부 서버측(FormRequest / Service)이다. 관리자 화면에서
|
||||
# auto_activate 를 보내는 곳은 번들 재설치 모달 하나뿐이고, 게이트를 네 경로 전부에
|
||||
# 걸기로 하면서(PO 결정 2026-08-04) 이 모달이 보내는 값을 운영자의 활성화 권한
|
||||
# 걸기로 하면서(프로젝트 결정 2026-08-04) 이 모달이 보내는 값을 운영자의 활성화 권한
|
||||
# (`abilities.can_activate`)으로 분기하도록 바꿨다 — 권한이 없으면 auto_activate 를
|
||||
# 보내지 않아 재설치는 그대로 되고 팩만 installed 로 내려간다.
|
||||
#
|
||||
|
||||
@@ -11,7 +11,7 @@ description: |
|
||||
설치 행은 있으나 파일이 사라진 드리프트 팩도 함께 복구한다(수동 복구 경로 겸용).
|
||||
- list()/Resource 는 파생 플래그 files_missing(active 인데 설치본 부재)과
|
||||
bundled_source_available(번들 소스로 복구 가능)을 부여해 드리프트 표면화 + 복구 가능 여부를 구분한다.
|
||||
- A2(인스톨러 기본 선택)는 admin_language 가 ko/en 한정이라 no-op → PO 결정으로 제외.
|
||||
- A2(인스톨러 기본 선택)는 admin_language 가 ko/en 한정이라 no-op → 프로젝트 결정으로 제외.
|
||||
|
||||
axes:
|
||||
target_locale_source: [supported_locales_default, explicit_locale_option]
|
||||
@@ -40,15 +40,12 @@ effects:
|
||||
- cli_list_surfaces_uninstalled_and_drift
|
||||
- admin_list_shows_drift_badge_and_reinstall
|
||||
- third_party_pack_without_bundle_source_not_offered_for_reinstall
|
||||
- supported_locale_without_bundle_source_is_statically_flagged
|
||||
- declared_locale_without_installed_pack_is_statically_flagged
|
||||
|
||||
test_files:
|
||||
- tests/Feature/LanguagePack/ProvisionLanguagePackCommandTest.php
|
||||
- tests/Feature/LanguagePack/ListDriftDetectionTest.php
|
||||
- tests/Feature/LanguagePack/ListLanguagePackCommandTest.php
|
||||
- templates/_bundled/sirsoft-admin_basic/__tests__/layouts/admin-language-pack-drift.test.tsx
|
||||
- .claude/scripts/audit/__tests__/rules.test.cjs
|
||||
|
||||
e2e_files:
|
||||
- tests/Playwright/specs/admin/language-pack-drift-discovery.spec.ts
|
||||
@@ -57,9 +54,15 @@ rules_layer_coverage:
|
||||
- rule: service-direct-data-access
|
||||
coverage: ProvisionLanguagePackCommand 은 LanguagePackService 만 주입 — Model/DB facade 직접 접근 없음
|
||||
- rule: lang-pack-supported-locale-has-bundle
|
||||
coverage: config/app.php supported_locales 비-base 로케일 ↔ g7-core-{locale} 번들 소스 정합 정적 검사
|
||||
coverage: |
|
||||
config/app.php supported_locales 비-base 로케일 ↔ g7-core-{locale} 번들 소스 정합 정적 검사.
|
||||
효과 supported_locale_without_bundle_source_is_statically_flagged 를 담당한다 — 런타임
|
||||
테스트가 아니라 정적 검사 계층에서만 성립하므로 effects 목록이 아닌 이 항목이 SSoT 다.
|
||||
- rule: lang-pack-installed-version-drift
|
||||
coverage: 선언 로케일의 설치본 부재(미설치 사각)를 프로비저닝된 환경에 한해 정적 검사
|
||||
coverage: |
|
||||
선언 로케일의 설치본 부재(미설치 사각)를 프로비저닝된 환경에 한해 정적 검사.
|
||||
효과 declared_locale_without_installed_pack_is_statically_flagged 를 담당한다 — 위와 동일한
|
||||
이유로 effects 목록이 아닌 이 항목이 SSoT 다.
|
||||
- rule: frontend-change-requires-e2e
|
||||
coverage: 관리자 언어팩 목록 partial 변경에 동반하는 language-pack-drift-discovery.spec.ts (템플릿 도메인)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user