@@ -10,6 +10,7 @@ import {
1010 ApiSearchResponseSchema ,
1111 parseArk ,
1212} from 'clawdhub-schema'
13+ import { unzipSync } from 'fflate'
1314import { describe , expect , it } from 'vitest'
1415import { readGlobalConfig } from '../packages/clawdhub/src/config'
1516
@@ -31,6 +32,15 @@ async function makeTempConfig(registry: string, token: string | null) {
3132}
3233
3334describe ( 'clawdhub e2e' , ( ) => {
35+ it ( 'prints CLI version via --cli-version' , async ( ) => {
36+ const result = spawnSync ( 'bun' , [ 'clawdhub' , '--cli-version' ] , {
37+ cwd : process . cwd ( ) ,
38+ encoding : 'utf8' ,
39+ } )
40+ expect ( result . status ) . toBe ( 0 )
41+ expect ( result . stdout . trim ( ) ) . toMatch ( / ^ \d + \. \d + \. \d + / )
42+ } )
43+
3444 it ( 'search endpoint returns a results array (schema parse)' , async ( ) => {
3545 const registry = process . env . CLAWDHUB_REGISTRY ?. trim ( ) || 'https://clawdhub.com'
3646 const url = new URL ( ApiRoutes . search , registry )
@@ -174,6 +184,7 @@ describe('clawdhub e2e', () => {
174184
175185 const cfg = await makeTempConfig ( registry , token )
176186 const workdir = await mkdtemp ( join ( tmpdir ( ) , 'clawdhub-e2e-publish-' ) )
187+ const installWorkdir = await mkdtemp ( join ( tmpdir ( ) , 'clawdhub-e2e-install-' ) )
177188 const slug = `e2e-${ Date . now ( ) } `
178189 const skillDir = join ( workdir , slug )
179190
@@ -241,6 +252,73 @@ describe('clawdhub e2e', () => {
241252 expect ( publish2 . status ) . toBe ( 0 )
242253 expect ( publish2 . stderr ) . not . toMatch ( / c h a n g e l o g r e q u i r e d / i)
243254
255+ const downloadUrl = new URL ( ApiRoutes . download , registry )
256+ downloadUrl . searchParams . set ( 'slug' , slug )
257+ downloadUrl . searchParams . set ( 'version' , '1.0.1' )
258+ const zipRes = await fetch ( downloadUrl . toString ( ) )
259+ expect ( zipRes . ok ) . toBe ( true )
260+ const zipBytes = new Uint8Array ( await zipRes . arrayBuffer ( ) )
261+ const unzipped = unzipSync ( zipBytes )
262+ expect ( Object . keys ( unzipped ) ) . toContain ( 'SKILL.md' )
263+
264+ const install = spawnSync (
265+ 'bun' ,
266+ [
267+ 'clawdhub' ,
268+ 'install' ,
269+ slug ,
270+ '--version' ,
271+ '1.0.0' ,
272+ '--force' ,
273+ '--site' ,
274+ site ,
275+ '--registry' ,
276+ registry ,
277+ '--workdir' ,
278+ installWorkdir ,
279+ ] ,
280+ {
281+ cwd : process . cwd ( ) ,
282+ env : { ...process . env , CLAWDHUB_CONFIG_PATH : cfg . path } ,
283+ encoding : 'utf8' ,
284+ } ,
285+ )
286+ expect ( install . status ) . toBe ( 0 )
287+
288+ const list = spawnSync (
289+ 'bun' ,
290+ [ 'clawdhub' , 'list' , '--site' , site , '--registry' , registry , '--workdir' , installWorkdir ] ,
291+ {
292+ cwd : process . cwd ( ) ,
293+ env : { ...process . env , CLAWDHUB_CONFIG_PATH : cfg . path } ,
294+ encoding : 'utf8' ,
295+ } ,
296+ )
297+ expect ( list . status ) . toBe ( 0 )
298+ expect ( list . stdout ) . toMatch ( new RegExp ( `${ slug } \\s+1\\.0\\.0` ) )
299+
300+ const update = spawnSync (
301+ 'bun' ,
302+ [
303+ 'clawdhub' ,
304+ 'update' ,
305+ slug ,
306+ '--force' ,
307+ '--site' ,
308+ site ,
309+ '--registry' ,
310+ registry ,
311+ '--workdir' ,
312+ installWorkdir ,
313+ ] ,
314+ {
315+ cwd : process . cwd ( ) ,
316+ env : { ...process . env , CLAWDHUB_CONFIG_PATH : cfg . path } ,
317+ encoding : 'utf8' ,
318+ } ,
319+ )
320+ expect ( update . status ) . toBe ( 0 )
321+
244322 const metaUrl = new URL ( ApiRoutes . skill , registry )
245323 metaUrl . searchParams . set ( 'slug' , slug )
246324 const metaRes = await fetch ( metaUrl . toString ( ) , { headers : { Accept : 'application/json' } } )
@@ -273,9 +351,6 @@ describe('clawdhub e2e', () => {
273351 } )
274352 expect ( metaAfterDelete . status ) . toBe ( 404 )
275353
276- const downloadUrl = new URL ( ApiRoutes . download , registry )
277- downloadUrl . searchParams . set ( 'slug' , slug )
278- downloadUrl . searchParams . set ( 'version' , '1.0.1' )
279354 const downloadAfterDelete = await fetch ( downloadUrl . toString ( ) )
280355 expect ( downloadAfterDelete . status ) . toBe ( 404 )
281356
@@ -330,6 +405,7 @@ describe('clawdhub e2e', () => {
330405 // best-effort cleanup
331406 }
332407 await rm ( workdir , { recursive : true , force : true } )
408+ await rm ( installWorkdir , { recursive : true , force : true } )
333409 await rm ( cfg . dir , { recursive : true , force : true } )
334410 }
335411 } , 180_000 )
0 commit comments