diff --git a/lib/exporter/zip.validation.test.ts b/lib/exporter/zip.validation.test.ts
new file mode 100644
index 0000000..c6ee0dc
--- /dev/null
+++ b/lib/exporter/zip.validation.test.ts
@@ -0,0 +1,44 @@
+import { describe, it, expect } from 'vitest'
+import JSZip from 'jszip'
+import { buildZip } from '@/lib/exporter/zip'
+
+const TE = new TextEncoder()
+
+describe('Export ZIP 검증 강화', () => {
+ it('assets가 비어있을 때 index.html/styles.css/app.js만 포함한다', async () => {
+ const zipBytes = await buildZip({
+ html: '
aok',
+ css: 'body{color:#111}',
+ js: 'console.log("ok")',
+ assets: {},
+ })
+ const zip = await JSZip.loadAsync(zipBytes)
+ const names = Object.keys(zip.files).sort()
+ expect(names).toEqual(['app.js','index.html','styles.css'])
+ })
+
+ it('assets가 있으면 모든 파일을 그대로 포함한다(서브폴더 포함)', async () => {
+ const assets = {
+ 'assets/img/logo.png': new Uint8Array([0,1,2,3]),
+ 'assets/svg/icon.svg': TE.encode(''),
+ 'assets/fonts/inter.woff2': new Uint8Array([4,5,6]),
+ }
+ const zipBytes = await buildZip({
+ html: 'aok',
+ css: 'body{color:#111}',
+ js: 'console.log("ok")',
+ assets,
+ })
+ const zip = await JSZip.loadAsync(zipBytes)
+ const names = Object.keys(zip.files)
+ expect(names).toContain('index.html')
+ expect(names).toContain('styles.css')
+ expect(names).toContain('app.js')
+ expect(names).toContain('assets/img/logo.png')
+ expect(names).toContain('assets/svg/icon.svg')
+ expect(names).toContain('assets/fonts/inter.woff2')
+
+ const logo = await zip.files['assets/img/logo.png'].async('uint8array')
+ expect(Array.from(logo)).toEqual([0,1,2,3])
+ })
+})