Test Specs CL - MZC-CSC/mc-web-console GitHub Wiki

Test Specs β€” CL 정리 (μžμ› μ‚­μ œ)

생성: 2026-10-16 Β· κΈ°μ€€: v07 μ„€μΉ˜ ν›„ 톡합 검증(IV) Β· 원본: 여정별 κ³„νšμ„œ(Excel) ← Test Scenarios Β· IV ν—ˆλΈŒ Β· κΈ°λŠ₯ TC

Playwright(TypeScript) spec μ†ŒμŠ€μž…λ‹ˆλ‹€. μ‹œλ‚˜λ¦¬μ˜€λ§ˆλ‹€ ## <μ‹œλ‚˜λ¦¬μ˜€λ²ˆν˜Έ> μ„Ήμ…˜μ΄ 있고 μ•΅μ»€λŠ” #<μ‹œλ‚˜λ¦¬μ˜€λ²ˆν˜Έ μ†Œλ¬Έμž> μž…λ‹ˆλ‹€. 곡용 ν—¬νΌλŠ” Test-Specs-Common. λΉ„λ°€λ²ˆν˜Έ 등은 λ§ˆμŠ€ν‚Ήν–ˆμŠ΅λ‹ˆλ‹€.

μ‹œλ‚˜λ¦¬μ˜€ 파일 쀄 ν•΄μ‹œ
CL_S01 tests/cl/CL_S01.spec.ts 49 c3e26c64
CL_S02 tests/cl/CL_S02.spec.ts 60 e5ce6dfb

CL_S01

tests/cl/CL_S01.spec.ts Β· 49쀄 Β· ν•΄μ‹œ c3e26c64 Β· μ‹œλ‚˜λ¦¬μ˜€ 보기

import { test, expect } from '../../lib/fixtures';
import { activeCsps, tid, k8sName, k8sProject } from '../../lib/data';
import { openK8sList, deleteClusterFromList, clusterRow, waitList } from '../../lib/k8s';

// CL_S01 : CSP ν…ŒμŠ€νŠΈ μžμ› 전체 μ‚­μ œ (λΉ„μš© λ°©μ§€, λ…Ήν™” 선택) [CSP별]
// **ν˜„μž¬λŠ” 5번(Project p{n} 의 K8s μ‚­μ œ)만 κ΅¬ν˜„**: λ‹€λ₯Έ 단계(NLB/MyImages/Disks/Infra/Projects/SSH Key/SG/vNet)λŠ” λ―Έκ΅¬ν˜„.
// K8s μ‚­μ œ: K8s Workloads ν΄λŸ¬μŠ€ν„° λͺ©λ‘ β†’ ν–‰μ˜ action item β†’ Delete (μ‚¬μš©μž μ•ˆλ‚΄ 2026-10-05).
// λŒ€μƒ ν΄λŸ¬μŠ€ν„°λŠ” ν™˜κ²½λ³€μˆ˜ CL_K8S(μ‰Όν‘œ ꡬ뢄)둜 μ§€μ •, μ—†μœΌλ©΄ k{n}d. 예) CL_K8S=k1d,k1f
for (const c of activeCsps()) {
  test(`${tid('CL_S01', c)} CSP ν…ŒμŠ€νŠΈ μžμ› 전체 μ‚­μ œ (K8s λ‹¨κ³„λ§Œ κ΅¬ν˜„)`, async ({ page }) => {
    test.skip(c.code === 'KT', 'KT Credential μ—†μŒ');
    const project = k8sProject(c);
    const targets = (process.env.CL_K8S ?? `${k8sName(c)}d`).split(',').map((s) => s.trim()).filter(Boolean);
    test.info().annotations.push({ type: 'scope', description: `ws01 / ${project} / K8s=${targets.join(',')} (CL_S01 5번만)` });

    await openK8sList(page, project);
    for (const name of targets) {
      // λͺ©λ‘μ€ μžλ™ κ°±μ‹ λ˜μ§€ μ•Šκ³  λ‘œλ”© 쀑 μΌμ‹œμ μœΌλ‘œ λΉ„μ–΄ 보일 수 μžˆλ‹€(직전 μ‚­μ œ 직후 'k1d μ—†μŒ'으둜 였판, 2026-10-08) β†’
      // λ©”λ‰΄λ‘œ λ‹€μ‹œ μ§„μž…ν•΄ 행이 λ‚˜νƒ€λ‚˜λŠ”μ§€ μ΅œλŒ€ ~60초 μž¬ν™•μΈν•œ 뒀에도 없을 λ•Œλ§Œ 'μ—†μŒ'으둜 λ³Έλ‹€
      let found = (await clusterRow(page, name).count()) > 0;
      for (let i = 0; i < 4 && !found; i++) {
        await page.waitForTimeout(10_000);
        await page.getByRole('link', { name: 'K8s Workloads' }).first().click();
        await waitList(page);
        found = (await clusterRow(page, name).count()) > 0;
      }
      if (!found) {
        test.info().annotations.push({ type: 'skip-not-found', description: `${name} λͺ©λ‘μ— μ—†μŒ β†’ κ±΄λ„ˆλœ€(μ‘΄μž¬ν•˜λŠ” κ²ƒλ§Œ μ‚­μ œ)` });
        continue;
      }
      await deleteClusterFromList(page, name); // action item β†’ Delete β†’ 확인 νŒμ—… Confirm (μš”μ²­ 직후 μƒνƒœλŠ” μ–΄λ…Έν…Œμ΄μ…˜)
      // μ‚­μ œ μš”μ²­ ν›„ 행이 μ‚¬λΌμ§€κ±°λ‚˜ μ‚­μ œ μ§„ν–‰ μƒνƒœ(Deleting λ“±)둜 λ°”λ€” λ•ŒκΉŒμ§€ μ΅œλŒ€ ~2λΆ„ 확인.
      // λͺ©λ‘μ€ μžλ™ κ°±μ‹ λ˜μ§€ μ•Šμ•„ μ‚¬μ΄λ“œ λ©”λ‰΄λ‘œ λ‹€μ‹œ μ§„μž…(Workspace/Project 선택 μœ μ§€)ν•΄μ„œ λ³Έλ‹€.
      const started = Date.now();
      let state = '';
      for (let i = 0; i < 8; i++) {
        await page.getByRole('link', { name: 'K8s Workloads' }).first().click();
        await waitList(page);
        const row = clusterRow(page, name);
        state = (await row.count()) ? (await row.first().innerText()).replace(/\s+/g, ' ') : '(ν–‰ μ—†μŒ)';
        if (/\(ν–‰ μ—†μŒ\)|Delet|Terminat/i.test(state)) break;
        await page.waitForTimeout(12_000);
      }
      test.info().annotations.push({ type: 'k8s-state-after', description: `${name}: ${state} (μš”μ²­ ν›„ ${Math.round((Date.now() - started) / 1000)}s)` });
      expect(state, `${name} μ‚­μ œ μš”μ²­ ν›„ μƒνƒœ(제거 λ˜λŠ” Deleting)`).toMatch(/\(ν–‰ μ—†μŒ\)|Delet|Terminat/i);
    }
  });
}

CL_S02

tests/cl/CL_S02.spec.ts Β· 60쀄 Β· ν•΄μ‹œ e5ce6dfb Β· μ‹œλ‚˜λ¦¬μ˜€ 보기

// CL_S02: λ©€ν‹° CSP Infra mc10 μ‚­μ œ (λΉ„μš© λ°©μ§€, λ…Ήν™” 선택) [정상, 곡톡] β€” TC-INFRA-056
// ν™”λ©΄: Infra Workloads λͺ©λ‘ β†’ mc10 ν–‰ 선택 β†’ λͺ©λ‘ μΉ΄λ“œ 'List of Infra' βŒ„ Terminate β†’ Confirm β†’ (Terminated) β†’ βŒ„ Delete β†’ Confirm β†’ λͺ©λ‘μ—μ„œ 제거
// μ‹œμž‘ μƒνƒœ: admin(mcmp) 둜그인 μƒνƒœ, Workspace=ws01 / Project=default μ„ νƒλœ μƒνƒœμ—μ„œ μ‹œμž‘. IV μ’…λ£Œ(IV3_S11 둜 λ§Œλ“  mc10 을 μ“°λŠ” IV4_S03Β·S06Β·S08 μ™„λ£Œ)
// λŒ€μƒ: Workspace ws01 / Project default(namespace default) 의 Infra mc10 ν•˜λ‚˜(IV3_S11 이 Workflow 둜 λ§Œλ“  8개 CSP NodeGroup g10-<csp>, node 각 1개)
//   β€” λ‹€λ₯Έ Infra(mc1~mc9, mw*, mt1)·곡용 vNet/SG/SSH Key(default-shared-*)λŠ” μ‚­μ œν•˜μ§€ μ•ŠμŒ
// 절차: 1.λͺ©λ‘μ—μ„œ mc10 선택 β†’ βŒ„ Terminate β†’ Confirm β†’ Status Terminated λŒ€κΈ° 2.βŒ„ Delete β†’ Confirm β†’ μ‚­μ œ 응닡(DelInfra) 기둝 3.λͺ©λ‘μ—μ„œ mc10 제거 확인
//   4.μž”μ—¬ μžμ› 확인: μ‚­μ œ 응닡에 '[WARNING] … node(s) may remain on the CSP' κ°€ μ—†μ–΄μ•Ό 함(Running μƒνƒœ force Delete μ‹œ μž”μ‘΄ κ²½κ³  사둀 2026-10-02 β†’ Terminate λ¨Όμ €)
// μž…λ ₯κ°’: Infra=mc10 / Workspace=ws01 / Project=default
// κΈ°λŒ€κ²°κ³Ό: 과금 μžμ› μž”μ‘΄ μ—†μŒ(mc10 λͺ©λ‘ 제거, 8개 CSP node μž”μ‘΄ κ²½κ³  μ—†μŒ). ν™”λ©΄μœΌλ‘œ 확인할 수 μ—†λŠ” CSP μ½˜μ†” μž”μ‘΄μ€ κ²°κ³Ό λ¬Έμ„œμ— μ‚¬μš©μž 확인 μš”μ²­μœΌλ‘œ 남김
// μ‹€ν–‰: μ‚¬μš©μž/메인 μ„Έμ…˜ μ§€μ‹œκ°€ μžˆμ„ λ•Œλ§Œ(2026-10-16 μž‘μ„± μ‹œμ μ—λŠ” μ‹€ν–‰ν•˜μ§€ μ•ŠμŒ)
// λΉ„κ³ : 2026-10-16 κ΅¬ν˜„(stub λŒ€μ²΄). Infra λ‹¨μœ„ μ œμ–΄λŠ” lib/iv3-mc10.ts infraListAction

import { test, expect } from '../../lib/fixtures';
import { tid } from '../../lib/data';
import { loginAsAdmin } from '../../lib/ui';
import { gotoInfraList, refreshInfraList, waitInfraListed, infraRow, infraRowText, waitInfraStatus } from '../../lib/iv4-infra';
import { MC10_NAME, infraListAction } from '../../lib/iv3-mc10';

test(`${tid('CL_S02')} λ©€ν‹° CSP Infra mc10 μ‚­μ œ (λΉ„μš© λ°©μ§€, λ…Ήν™” 선택)`, async ({ page }) => {
  test.setTimeout(60 * 60_000);
  const name = process.env.CL_S02_INFRA ?? MC10_NAME;
  test.info().annotations.push({ type: 'scope', description: `ws01 / default(namespace default) / Infra=${name} 만 μ‚­μ œ` });
  await loginAsAdmin(page);
  await gotoInfraList(page);
  test.skip(!(await waitInfraListed(page, name)), `${name} λͺ©λ‘μ— μ—†μŒ(이미 μ‚­μ œλκ±°λ‚˜ IV3_S11 λ―Έμ‹€ν–‰)`);
  const before = await infraRowText(page, name);
  test.info().annotations.push({ type: 'list-before', description: before });

  // 1. Terminate β†’ Terminated
  if (!/^Terminated/i.test(before)) {
    await infraListAction(page, name, 'Terminate');
    await gotoInfraList(page);
    const t = await waitInfraStatus(page, name, /^Terminated/i, 30 * 60_000);
    test.info().annotations.push({ type: 'terminated', description: t });
  }

  // 2. Delete β†’ 응닡 기둝
  const del: string[] = [];
  const onResp = async (r: import('@playwright/test').Response) => {
    if (/DelInfra|DeleteInfra|DelMci/i.test(r.url())) del.push(`${r.status()} ${(await r.text().catch(() => '')).replace(/\s+/g, ' ').slice(0, 1500)}`);
  };
  page.on('response', onResp);
  await infraListAction(page, name, 'Delete');
  // 3. λͺ©λ‘μ—μ„œ 제거(μ΅œλŒ€ 10λΆ„)
  let row = 'x';
  for (let i = 0; i < 20 && row; i++) {
    await page.waitForTimeout(30_000);
    await refreshInfraList(page);
    await page.waitForTimeout(3000);
    row = (await infraRow(page, name).count()) ? await infraRowText(page, name) : '';
  }
  page.off('response', onResp);
  test.info().annotations.push({ type: 'delete-response', description: del.join(' || ') || '(응닡 λ―Έμˆ˜μ§‘)' });
  test.info().annotations.push({ type: 'list-after', description: row || `${name} λͺ©λ‘μ—μ„œ 제거` });
  expect(row, `${name} λͺ©λ‘μ—μ„œ 제거`).toBe('');
  // 4. μž”μ—¬ μžμ› κ²½κ³  μ—†μŒ
  expect(del.join(' '), 'μ‚­μ œ 응닡에 CSP μž”μ‘΄ κ²½κ³  μ—†μŒ').not.toMatch(/may remain|WARNING/i);
  await page.waitForTimeout(3000); // λ…Ήν™” 마무리
});
⚠️ **GitHub.com Fallback** ⚠️