@@ -629,6 +629,110 @@ func TestManagerUpload_ImplicitSecondaryReplacement(t *testing.T) {
629629 }
630630}
631631
632+ func TestServiceUploadReplacementRejectsInvalidTargets (t * testing.T ) {
633+ data := bytes .Repeat ([]byte ("rq" ), 128 )
634+ info , err := piece .CalculateFromBytes (data )
635+ if err != nil {
636+ t .Fatalf ("CalculateFromBytes: %v" , err )
637+ }
638+
639+ newPrimary := func () * fakeUploadContext {
640+ return & fakeUploadContext {
641+ id : types .NewBigInt (101 ),
642+ endpoint : "https://primary.example.com" ,
643+ pieceURL : "https://primary.example.com/piece/" + info .CIDv2 .String (),
644+ storeFn : func (_ context.Context , _ io.Reader , _ * StoreOptions ) (* StoreResult , error ) {
645+ return & StoreResult {PieceCID : info .CIDv2 , Size : int64 (len (data ))}, nil
646+ },
647+ commitFn : func (_ context.Context , _ CommitRequest ) (* CommitResult , error ) {
648+ return & CommitResult {DataSetID : types .NewBigInt (1001 ), PieceIDs : []types.BigInt {types .NewBigInt (2001 )}, IsNewDataSet : true }, nil
649+ },
650+ }
651+ }
652+ newFailedSecondary := func () * fakeUploadContext {
653+ return & fakeUploadContext {
654+ id : types .NewBigInt (202 ),
655+ endpoint : "https://secondary.example.com" ,
656+ presignFn : func (_ context.Context , _ []PieceInput ) ([]byte , error ) {
657+ return []byte {0x01 }, nil
658+ },
659+ pullFn : func (_ context.Context , _ PullRequest ) (* PullResult , error ) {
660+ return nil , errors .New ("pull failed" )
661+ },
662+ }
663+ }
664+
665+ wrongIdentity := serviceTestIdentity ()
666+ wrongIdentity .Payer = common .HexToAddress ("0x9999" )
667+ tests := map [string ]struct {
668+ replacement StorageContext
669+ exclude []types.BigInt
670+ }{
671+ "nil-core" : {replacement : & ProviderContext {}},
672+ "typed nil" : {replacement : (* ProviderContext )(nil )},
673+ "identity mismatch" : {replacement : & fakeUploadContext {
674+ id : types .NewBigInt (303 ),
675+ endpoint : "https://replacement.example.com" ,
676+ identity : & wrongIdentity ,
677+ presignFn : func (_ context.Context , _ []PieceInput ) ([]byte , error ) {
678+ t .Fatal ("PresignForCommit must not run for an identity-mismatched replacement" )
679+ return nil , nil
680+ },
681+ }},
682+ "excluded" : {
683+ replacement : & fakeUploadContext {
684+ id : types .NewBigInt (303 ),
685+ endpoint : "https://replacement.example.com" ,
686+ presignFn : func (_ context.Context , _ []PieceInput ) ([]byte , error ) {
687+ t .Fatal ("PresignForCommit must not run for an excluded replacement" )
688+ return nil , nil
689+ },
690+ },
691+ exclude : []types.BigInt {types .NewBigInt (303 )},
692+ },
693+ "duplicate primary" : {replacement : & fakeUploadContext {
694+ id : types .NewBigInt (101 ),
695+ endpoint : "https://replacement.example.com" ,
696+ presignFn : func (_ context.Context , _ []PieceInput ) ([]byte , error ) {
697+ t .Fatal ("PresignForCommit must not run for a duplicate replacement" )
698+ return nil , nil
699+ },
700+ }},
701+ }
702+
703+ for name , tt := range tests {
704+ t .Run (name , func (t * testing.T ) {
705+ defer func () {
706+ if recovered := recover (); recovered != nil {
707+ t .Fatalf ("Upload panicked: %v" , recovered )
708+ }
709+ }()
710+ svc := mustNewService (t , Options {
711+ Resolver : & fakeResolver {
712+ contexts : []StorageContext {newPrimary (), newFailedSecondary ()},
713+ replacements : []StorageContext {tt .replacement },
714+ },
715+ })
716+ got , err := svc .Upload (context .Background (), bytes .NewReader (data ), & UploadOptions {
717+ Copies : 2 ,
718+ ExcludeProviderIDs : tt .exclude ,
719+ })
720+ if err != nil {
721+ t .Fatalf ("Upload: %v" , err )
722+ }
723+ if got .Complete || got .SuccessCount () != 1 {
724+ t .Fatalf ("complete=%v success=%d want partial primary" , got .Complete , got .SuccessCount ())
725+ }
726+ if len (got .FailedAttempts ) < 2 {
727+ t .Fatalf ("failedAttempts=%+v want secondary failure and rejected replacement" , got .FailedAttempts )
728+ }
729+ if ! errors .Is (got .FailedAttempts [len (got .FailedAttempts )- 1 ].Err , ErrInvalidArgument ) {
730+ t .Fatalf ("replacement error=%v want ErrInvalidArgument" , got .FailedAttempts [len (got .FailedAttempts )- 1 ].Err )
731+ }
732+ })
733+ }
734+ }
735+
632736func TestManagerUpload_ReplacementKeepsImmutableClientDataSetID (t * testing.T ) {
633737 data := bytes .Repeat ([]byte ("ij" ), 128 )
634738 info , err := piece .CalculateFromBytes (data )
0 commit comments