44package charms_test
55
66import (
7+ "archive/zip"
78 "os"
89
910 "github.com/juju/charm/v12"
@@ -287,7 +288,7 @@ func (s *addCharmSuite) TestAddCharm(c *gc.C) {
287288 c .Assert (got , gc .DeepEquals , origin )
288289}
289290
290- func (s charmsMockSuite ) TestCheckCharmPlacement (c * gc.C ) {
291+ func (s * charmsMockSuite ) TestCheckCharmPlacement (c * gc.C ) {
291292 ctrl := gomock .NewController (c )
292293 defer ctrl .Finish ()
293294
@@ -311,7 +312,7 @@ func (s charmsMockSuite) TestCheckCharmPlacement(c *gc.C) {
311312 c .Assert (err , jc .ErrorIsNil )
312313}
313314
314- func (s charmsMockSuite ) TestCheckCharmPlacementError (c * gc.C ) {
315+ func (s * charmsMockSuite ) TestCheckCharmPlacementError (c * gc.C ) {
315316 ctrl := gomock .NewController (c )
316317 defer ctrl .Finish ()
317318
@@ -411,7 +412,7 @@ func (s *charmsMockSuite) TestZipHasDispatchFileOnly(c *gc.C) {
411412 c .Assert (hasDispatch , jc .IsTrue )
412413}
413414
414- func (s * charmsMockSuite ) TestZipHasNoHooksNorDispath (c * gc.C ) {
415+ func (s * charmsMockSuite ) TestZipHasNoHooksNorDispatch (c * gc.C ) {
415416 ch := testcharms .Repo .CharmDir ("category" ) // has no hooks nor dispatch file
416417 tempFile , err := os .CreateTemp (c .MkDir (), "charm" )
417418 c .Assert (err , jc .ErrorIsNil )
@@ -424,3 +425,91 @@ func (s *charmsMockSuite) TestZipHasNoHooksNorDispath(c *gc.C) {
424425 c .Assert (err , jc .ErrorIsNil )
425426 c .Assert (hasHooks , jc .IsFalse )
426427}
428+
429+ // TestZipHasSingleHook tests that an archive containing only a single hook
430+ // file (and no zip entry for the hooks directory) is still validated as a
431+ // charm with hooks.
432+ func (s * charmsMockSuite ) TestZipHasSingleHook (c * gc.C ) {
433+ tempFile , err := os .CreateTemp (c .MkDir (), "charm" )
434+ c .Assert (err , jc .ErrorIsNil )
435+ defer tempFile .Close ()
436+
437+ zipWriter := zip .NewWriter (tempFile )
438+ // add a single install hook
439+ _ , err = zipWriter .Create ("hooks/install" )
440+ c .Assert (err , jc .ErrorIsNil )
441+ err = zipWriter .Close ()
442+ c .Assert (err , jc .ErrorIsNil )
443+
444+ // Verify created zip is as expected
445+ zipReader , err := zip .OpenReader (tempFile .Name ())
446+ c .Assert (err , jc .ErrorIsNil )
447+ c .Assert (len (zipReader .File ), gc .Equals , 1 )
448+ c .Assert (zipReader .File [0 ].Name , gc .Equals , "hooks/install" )
449+ c .Assert (zipReader .File [0 ].Mode ().IsRegular (), jc .IsTrue )
450+
451+ // Verify this is validated as having a hook
452+ hasHooks , err := (* charms .HasHooksOrDispatch )(tempFile .Name ())
453+ c .Check (err , jc .ErrorIsNil )
454+ c .Check (hasHooks , jc .IsTrue )
455+ }
456+
457+ // TestZipEmptyHookDir tests that an archive containing only an empty hooks
458+ // directory is not validated as a charm with hooks.
459+ func (s * charmsMockSuite ) TestZipEmptyHookDir (c * gc.C ) {
460+ tempFile , err := os .CreateTemp (c .MkDir (), "charm" )
461+ c .Assert (err , jc .ErrorIsNil )
462+ defer tempFile .Close ()
463+
464+ zipWriter := zip .NewWriter (tempFile )
465+ // add an empty hooks directory
466+ _ , err = zipWriter .Create ("hooks/" )
467+ c .Assert (err , jc .ErrorIsNil )
468+ err = zipWriter .Close ()
469+ c .Assert (err , jc .ErrorIsNil )
470+
471+ // Verify created zip is as expected
472+ zipReader , err := zip .OpenReader (tempFile .Name ())
473+ c .Assert (err , jc .ErrorIsNil )
474+ c .Assert (len (zipReader .File ), gc .Equals , 1 )
475+ c .Assert (zipReader .File [0 ].Name , gc .Equals , "hooks/" )
476+ c .Assert (zipReader .File [0 ].Mode ().IsDir (), jc .IsTrue )
477+
478+ // Verify this is validated as having no hooks
479+ hasHooks , err := (* charms .HasHooksOrDispatch )(tempFile .Name ())
480+ c .Check (err , jc .ErrorIsNil )
481+ c .Check (hasHooks , jc .IsFalse )
482+ }
483+
484+ // TestZipSubfileHook tests that an archive containing nested subfiles inside
485+ // the hooks directory (i.e. not in the top level) is not validated as a charm
486+ // with hooks.
487+ func (s * charmsMockSuite ) TestZipSubfileHook (c * gc.C ) {
488+ tempFile , err := os .CreateTemp (c .MkDir (), "charm" )
489+ c .Assert (err , jc .ErrorIsNil )
490+ defer tempFile .Close ()
491+
492+ zipWriter := zip .NewWriter (tempFile )
493+ // add some files inside a subdir of hooks
494+ _ , err = zipWriter .Create ("hooks/foo/bar.sh" )
495+ c .Assert (err , jc .ErrorIsNil )
496+ _ , err = zipWriter .Create ("hooks/hooks/install" )
497+ c .Assert (err , jc .ErrorIsNil )
498+ _ , err = zipWriter .Create ("foo/hooks/install" )
499+ c .Assert (err , jc .ErrorIsNil )
500+ err = zipWriter .Close ()
501+ c .Assert (err , jc .ErrorIsNil )
502+
503+ // Verify created zip is as expected
504+ zipReader , err := zip .OpenReader (tempFile .Name ())
505+ c .Assert (err , jc .ErrorIsNil )
506+ c .Assert (len (zipReader .File ), gc .Equals , 3 )
507+ for _ , f := range zipReader .File {
508+ c .Assert (f .Mode ().IsRegular (), jc .IsTrue )
509+ }
510+
511+ // Verify this is not validated as having a hook
512+ hasHooks , err := (* charms .HasHooksOrDispatch )(tempFile .Name ())
513+ c .Check (err , jc .ErrorIsNil )
514+ c .Check (hasHooks , jc .IsFalse )
515+ }
0 commit comments