@@ -120,7 +120,7 @@ func TestSaveArtifact_ValidationErrors(t *testing.T) {
120120
121121func TestSaveArtifact_WithProvenance (t * testing.T ) {
122122 store := newInMemoryAssetStore ()
123- tk := New (Config {Name : "test" , AssetStore : store , S3Bucket : "bucket" })
123+ tk := New (Config {Name : "test" , AssetStore : store , S3Client : & mockS3Client {}, S3Bucket : "bucket" })
124124
125125 provCalls := []middleware.ProvenanceToolCall {
126126 {ToolName : "trino_query" , Timestamp : "2024-01-01T00:00:00Z" , Summary : "SELECT 1" },
@@ -305,6 +305,26 @@ func TestManageArtifact_MissingAssetID(t *testing.T) {
305305 }
306306}
307307
308+ func TestBuildSaveOutput (t * testing.T ) {
309+ tk := New (Config {Name : "test" , BaseURL : "https://example.com" , S3Bucket : "bucket" })
310+ out := tk .buildSaveOutput ("abc123" , portal.Provenance {
311+ ToolCalls : []portal.ProvenanceToolCall {{ToolName : "trino_query" }},
312+ })
313+
314+ assert .Equal (t , "abc123" , out .AssetID )
315+ assert .Equal (t , "https://example.com/portal/assets/abc123" , out .PortalURL )
316+ assert .True (t , out .ProvenanceCaptured )
317+ assert .Equal (t , 1 , out .ToolCallsRecorded )
318+ }
319+
320+ func TestBuildSaveOutputNoBaseURL (t * testing.T ) {
321+ tk := New (Config {Name : "test" , S3Bucket : "bucket" })
322+ out := tk .buildSaveOutput ("abc123" , portal.Provenance {})
323+
324+ assert .Empty (t , out .PortalURL )
325+ assert .False (t , out .ProvenanceCaptured )
326+ }
327+
308328func TestExtensionForContentType (t * testing.T ) {
309329 tests := []struct {
310330 ct string
@@ -444,7 +464,7 @@ func TestSaveArtifact_S3Error(t *testing.T) {
444464
445465func TestSaveArtifact_NoContext (t * testing.T ) {
446466 store := newInMemoryAssetStore ()
447- tk := New (Config {Name : "test" , AssetStore : store , S3Bucket : "bucket" })
467+ tk := New (Config {Name : "test" , AssetStore : store , S3Client : & mockS3Client {}, S3Bucket : "bucket" })
448468
449469 // Call without PlatformContext — should default to anonymous.
450470 input := saveArtifactInput {
@@ -456,6 +476,25 @@ func TestSaveArtifact_NoContext(t *testing.T) {
456476 assert .False (t , result .IsError )
457477}
458478
479+ func TestSaveArtifact_NilS3Client (t * testing.T ) {
480+ tk := New (Config {Name : "test" , AssetStore : newInMemoryAssetStore (), S3Client : nil , S3Bucket : "bucket" })
481+
482+ ctx := middleware .WithPlatformContext (context .Background (), & middleware.PlatformContext {
483+ UserID : "user1" , SessionID : "sess1" ,
484+ })
485+
486+ input := saveArtifactInput {
487+ Name : "Test" , Content : "<div/>" , ContentType : "text/html" ,
488+ }
489+
490+ result , _ , err := tk .handleSaveArtifact (ctx , nil , input )
491+ require .NoError (t , err )
492+ assert .True (t , result .IsError )
493+ tc , ok := result .Content [0 ].(* mcp.TextContent ) //nolint:errcheck // test assertion
494+ require .True (t , ok )
495+ assert .Contains (t , tc .Text , "content storage not configured" )
496+ }
497+
459498func TestManageArtifact_DeleteWrongOwner (t * testing.T ) {
460499 store := newInMemoryAssetStore ()
461500 _ = store .Insert (context .Background (), portal.Asset {
@@ -591,7 +630,7 @@ func (s *errorAssetStore) Update(_ context.Context, _ string, _ portal.AssetUpda
591630func TestSaveArtifact_StoreInsertError (t * testing.T ) {
592631 store := & errorAssetStore {insertErr : notFoundError {}}
593632 store .assets = make (map [string ]portal.Asset )
594- tk := New (Config {Name : "test" , AssetStore : store , S3Bucket : "bucket" })
633+ tk := New (Config {Name : "test" , AssetStore : store , S3Client : & mockS3Client {}, S3Bucket : "bucket" })
595634
596635 ctx := middleware .WithPlatformContext (context .Background (), & middleware.PlatformContext {
597636 UserID : "user1" , SessionID : "sess1" ,
@@ -647,6 +686,27 @@ func TestManageArtifact_UpdateStoreError(t *testing.T) {
647686 assert .Contains (t , tc .Text , "failed to update asset" )
648687}
649688
689+ func TestManageArtifact_UpdateNilS3Client (t * testing.T ) {
690+ store := newInMemoryAssetStore ()
691+ _ = store .Insert (context .Background (), portal.Asset {
692+ ID : "a1" , OwnerID : "user1" , Name : "Test" , ContentType : "text/html" ,
693+ Tags : []string {}, Provenance : portal.Provenance {},
694+ })
695+
696+ tk := New (Config {Name : "test" , AssetStore : store , S3Client : nil , S3Bucket : "bucket" })
697+
698+ ctx := middleware .WithPlatformContext (context .Background (), & middleware.PlatformContext {UserID : "user1" })
699+
700+ result , _ , err := tk .handleManageArtifact (ctx , nil , manageArtifactInput {
701+ Action : "update" , AssetID : "a1" , Content : "<div>Updated</div>" ,
702+ })
703+ require .NoError (t , err )
704+ assert .True (t , result .IsError )
705+ tc , ok := result .Content [0 ].(* mcp.TextContent ) //nolint:errcheck // test assertion
706+ require .True (t , ok )
707+ assert .Contains (t , tc .Text , "content storage not configured" )
708+ }
709+
650710func TestManageArtifact_UpdateWithContentError (t * testing.T ) {
651711 store := newInMemoryAssetStore ()
652712 _ = store .Insert (context .Background (), portal.Asset {
0 commit comments