diff --git a/Package.swift b/Package.swift index bd46c8510..210d34b57 100644 --- a/Package.swift +++ b/Package.swift @@ -583,6 +583,12 @@ let package = Package( ], path: "Sources/Services/MachineAPIService/Client" ), + .testTarget( + name: "MachineAPIClientTests", + dependencies: [ + "MachineAPIClient" + ] + ), .target( name: "MachineAPIService", dependencies: [ diff --git a/Sources/Services/MachineAPIService/Client/MachineClient.swift b/Sources/Services/MachineAPIService/Client/MachineClient.swift index 7f3167ff4..e8d7da5c0 100644 --- a/Sources/Services/MachineAPIService/Client/MachineClient.swift +++ b/Sources/Services/MachineAPIService/Client/MachineClient.swift @@ -390,10 +390,18 @@ extension MachineClient { } } - if var resources, let setupScript { - resources.setupScript = setupScript - } + return merge(resources: resources, setupScript: setupScript) + } + /// Merge a separately-fetched setup script into the decoded machine resources. + /// + /// `MachineResources` is a value type, so mutating a copy bound via `if var` + /// would not affect the returned value. Assign through the optional instead. + static func merge(resources: MachineResources?, setupScript: String?) -> MachineResources? { + var resources = resources + if let setupScript { + resources?.setupScript = setupScript + } return resources } } diff --git a/Tests/MachineAPIClientTests/MachineClientMergeTests.swift b/Tests/MachineAPIClientTests/MachineClientMergeTests.swift new file mode 100644 index 000000000..24f80acd3 --- /dev/null +++ b/Tests/MachineAPIClientTests/MachineClientMergeTests.swift @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// Copyright © 2026 Apple Inc. and the container project authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +//===----------------------------------------------------------------------===// + +import Testing + +@testable import MachineAPIClient + +struct MachineClientMergeTests { + @Test func mergeAppliesSetupScriptToResources() throws { + let resources = MachineResources(schemaVersion: 1, shell: "/bin/sh") + let merged = MachineClient.merge(resources: resources, setupScript: "echo hello") + #expect(merged?.setupScript == "echo hello") + #expect(merged?.shell == "/bin/sh") + } + + @Test func mergeLeavesResourcesUnchangedWhenNoSetupScript() throws { + let resources = MachineResources(schemaVersion: 1, setupScript: "existing") + let merged = MachineClient.merge(resources: resources, setupScript: nil) + #expect(merged?.setupScript == "existing") + } + + @Test func mergeReturnsNilWhenNoResources() throws { + let merged = MachineClient.merge(resources: nil, setupScript: "echo hello") + #expect(merged == nil) + } +}