@@ -3006,6 +3006,130 @@ func TestPreparePlainBundleCommon_TrimmedAgeEncryptionTriggersDecrypt(t *testing
30063006 }
30073007}
30083008
3009+ func TestPreparePlainBundleCommon_AgeModeRequiresAgeSuffix (t * testing.T ) {
3010+ origFS := restoreFS
3011+ restoreFS = osFS {}
3012+ t .Cleanup (func () { restoreFS = origFS })
3013+
3014+ dir := t .TempDir ()
3015+ workArchive := filepath .Join (dir , "backup.tar.xz" )
3016+ if err := os .WriteFile (workArchive , []byte ("ciphertext" ), 0o600 ); err != nil {
3017+ t .Fatalf ("write archive: %v" , err )
3018+ }
3019+ manifestPath := filepath .Join (dir , "backup.metadata" )
3020+ if err := os .WriteFile (manifestPath , []byte (`{"encryption_mode":"age"}` ), 0o600 ); err != nil {
3021+ t .Fatalf ("write manifest: %v" , err )
3022+ }
3023+ checksumPath := filepath .Join (dir , "backup.sha256" )
3024+ checksumLine := checksumLineForBytes (filepath .Base (workArchive ), []byte ("ciphertext" ))
3025+ if err := os .WriteFile (checksumPath , []byte (checksumLine ), 0o600 ); err != nil {
3026+ t .Fatalf ("write checksum: %v" , err )
3027+ }
3028+
3029+ cand := & decryptCandidate {
3030+ Manifest : & backup.Manifest {
3031+ ArchivePath : workArchive ,
3032+ EncryptionMode : "age" ,
3033+ },
3034+ Source : sourceRaw ,
3035+ RawArchivePath : workArchive ,
3036+ RawMetadataPath : manifestPath ,
3037+ RawChecksumPath : checksumPath ,
3038+ DisplayBase : "backup.tar.xz" ,
3039+ }
3040+
3041+ logger := logging .New (types .LogLevelError , false )
3042+ logger .SetOutput (io .Discard )
3043+
3044+ decryptCalled := false
3045+ _ , err := preparePlainBundleCommon (context .Background (), cand , "1.0.0" , logger , func (ctx context.Context , encryptedPath , outputPath , displayName string ) error {
3046+ decryptCalled = true
3047+ return nil
3048+ })
3049+ if err == nil {
3050+ t .Fatal ("preparePlainBundleCommon error = nil; want missing .age suffix error" )
3051+ }
3052+ if ! strings .Contains (err .Error (), "missing .age suffix" ) {
3053+ t .Fatalf ("preparePlainBundleCommon error = %v; want missing .age suffix error" , err )
3054+ }
3055+ if decryptCalled {
3056+ t .Fatal ("decrypt callback was called for archive without .age suffix" )
3057+ }
3058+ }
3059+
3060+ func TestPreparePlainBundleCommon_NonAgeRejectsAgeSuffix (t * testing.T ) {
3061+ origFS := restoreFS
3062+ restoreFS = osFS {}
3063+ t .Cleanup (func () { restoreFS = origFS })
3064+
3065+ dir := t .TempDir ()
3066+ workArchive := filepath .Join (dir , "backup.tar.xz.age" )
3067+ if err := os .WriteFile (workArchive , []byte ("ciphertext" ), 0o600 ); err != nil {
3068+ t .Fatalf ("write archive: %v" , err )
3069+ }
3070+ manifestPath := filepath .Join (dir , "backup.metadata" )
3071+ if err := os .WriteFile (manifestPath , []byte (`{"encryption_mode":"none"}` ), 0o600 ); err != nil {
3072+ t .Fatalf ("write manifest: %v" , err )
3073+ }
3074+ checksumPath := filepath .Join (dir , "backup.sha256" )
3075+ checksumLine := checksumLineForBytes (filepath .Base (workArchive ), []byte ("ciphertext" ))
3076+ if err := os .WriteFile (checksumPath , []byte (checksumLine ), 0o600 ); err != nil {
3077+ t .Fatalf ("write checksum: %v" , err )
3078+ }
3079+
3080+ cand := & decryptCandidate {
3081+ Manifest : & backup.Manifest {
3082+ ArchivePath : workArchive ,
3083+ EncryptionMode : "none" ,
3084+ },
3085+ Source : sourceRaw ,
3086+ RawArchivePath : workArchive ,
3087+ RawMetadataPath : manifestPath ,
3088+ RawChecksumPath : checksumPath ,
3089+ DisplayBase : "backup.tar.xz.age" ,
3090+ }
3091+
3092+ logger := logging .New (types .LogLevelError , false )
3093+ logger .SetOutput (io .Discard )
3094+
3095+ _ , err := preparePlainBundleCommon (context .Background (), cand , "1.0.0" , logger , func (ctx context.Context , encryptedPath , outputPath , displayName string ) error {
3096+ t .Fatal ("decrypt callback should not be called for non-age archive handling" )
3097+ return nil
3098+ })
3099+ if err == nil {
3100+ t .Fatal ("preparePlainBundleCommon error = nil; want .age suffix mismatch error" )
3101+ }
3102+ if ! strings .Contains (err .Error (), "has .age suffix but encryption mode is none" ) {
3103+ t .Fatalf ("preparePlainBundleCommon error = %v; want .age suffix mismatch error" , err )
3104+ }
3105+ }
3106+
3107+ func TestResolvePreparedArchivePath_AgeFallbackUsesUniqueOutput (t * testing.T ) {
3108+ origFS := restoreFS
3109+ restoreFS = osFS {}
3110+ t .Cleanup (func () { restoreFS = origFS })
3111+
3112+ workDir := t .TempDir ()
3113+ stagedArchivePath := filepath .Join (workDir , ".age" )
3114+
3115+ got , err := resolvePreparedArchivePath (workDir , stagedArchivePath , "age" )
3116+ if err != nil {
3117+ t .Fatalf ("resolvePreparedArchivePath error: %v" , err )
3118+ }
3119+ if got == stagedArchivePath {
3120+ t .Fatalf ("resolvePreparedArchivePath() = %q; want unique output path" , got )
3121+ }
3122+ if got == workDir {
3123+ t .Fatalf ("resolvePreparedArchivePath() = %q; want file path inside workdir" , got )
3124+ }
3125+ if filepath .Dir (got ) != workDir {
3126+ t .Fatalf ("resolvePreparedArchivePath() dir = %q; want %q" , filepath .Dir (got ), workDir )
3127+ }
3128+ if ! strings .HasPrefix (filepath .Base (got ), ".age.decrypted-" ) {
3129+ t .Fatalf ("resolvePreparedArchivePath() base = %q; want .age.decrypted-*" , filepath .Base (got ))
3130+ }
3131+ }
3132+
30093133// =====================================
30103134// extractBundleToWorkdirWithLogger coverage tests
30113135// =====================================
0 commit comments