From 1c38d6ccd8286525e9ec755f7e24d6e0565a29f6 Mon Sep 17 00:00:00 2001 From: operate-services-sdk-bot Date: Tue, 11 Jul 2023 18:24:27 +0000 Subject: [PATCH] Release v1.0.0-beta.6 --- CHANGELOG.md | 12 +- Dockerfile | 19 + ...eletePlayerPolicyStatementsHandlerTests.cs | 8 +- ...leteProjectPolicyStatementsHandlerTests.cs | 8 +- .../GetAllPlayerPoliciesHandlerTests.cs | 4 +- .../Handlers/GetPlayerPolicyHandlerTests.cs | 4 +- .../Handlers/GetProjectPolicyHandlerTests.cs | 4 +- .../UpsertPlayerPolicyHandlerTests.cs | 8 +- .../UpsertProjectPolicyHandlerTests.cs | 8 +- .../DeletePlayerPolicyStatementsHandler.cs | 2 +- .../DeleteProjectPolicyStatementsHandler.cs | 2 +- .../Handlers/GetAllPlayerPoliciesHandler.cs | 2 +- .../Handlers/GetPlayerPolicyHandler.cs | 2 +- .../Handlers/GetProjectPolicyHandler.cs | 2 +- .../Handlers/UpsertPlayerPolicyHandler.cs | 2 +- .../Handlers/UpsertProjectPolicyHandler.cs | 2 +- .../Handlers/DeployHandlerTests.cs | 2 +- .../Export/BaseExporter.cs | 4 +- .../Handlers/DeployHandler.cs | 2 +- .../Import/BaseImporter.cs | 2 +- .../Fetch/JavaScriptFetchServiceTests.cs | 2 +- .../Handlers/CreateHandlerTests.cs | 4 +- .../Handlers/DeleteHandlerTests.cs | 4 +- .../Handlers/ExportModulesHandlerTests.cs | 2 +- .../Handlers/ExportScriptsHandlerTests.cs | 2 +- .../Handlers/GetHandlerTests.cs | 4 +- .../Handlers/GetModuleHandlerTests.cs | 4 +- .../Handlers/ImportModulesHandlerTests.cs | 36 +- .../Handlers/ImportScriptsHandlerTests.cs | 37 +- .../Handlers/ListHandlerTests.cs | 4 +- .../Handlers/ListModuleHandlerTests.cs | 4 +- .../Handlers/PublishHandlerTests.cs | 4 +- .../Handlers/UpdateHandlerTests.cs | 4 +- .../Authoring/Fetch/JavaScriptFetchService.cs | 2 +- .../Handlers/CreateHandler.cs | 2 +- .../Handlers/DeleteHandler.cs | 2 +- .../Handlers/DeleteModuleHandler.cs | 2 +- .../Handlers/GetHandler.cs | 2 +- .../Handlers/GetModuleHandler.cs | 2 +- .../Handlers/ListHandler.cs | 4 +- .../Handlers/ListModulesHandler.cs | 2 +- .../Handlers/PublishHandler.cs | 2 +- .../Handlers/UpdateHandler.cs | 2 +- .../Console/CliPromptTest.cs | 2 +- .../Unity.Services.Cli.Common/CommonModule.cs | 2 +- .../Validator/ConfigurationValidator.cs | 6 +- .../Console/CliPrompt.cs | 4 +- .../Console/ICliPrompt.cs | 6 + .../AnalyticEvent/AnalyticEventUtils.cs | 6 + .../Unity.Services.Cli.Common.csproj | 2 +- .../Utils/IUnityEnvironment.cs | 7 +- .../Handlers/DeletionHandlerTests.cs | 2 +- .../UnityEnvironmentTests.cs | 6 +- .../Handlers/DeletionHandler.cs | 2 +- .../Service/UnityEnvironment.cs | 8 +- .../GameServerHostingModuleTests.cs | 213 ++++++ .../GameServerHostingUnitTestsConstants.cs | 74 ++ .../BuildConfigurationCreateHandlerTests.cs | 251 +++++++ .../BuildConfigurationDeleteHandlerTests.cs | 119 ++++ .../BuildConfigurationGetHandlerTests.cs | 172 +++++ .../BuildConfigurationListHandlerTests.cs | 75 ++ .../BuildConfigurationUpdateHandlerTests.cs | 87 +++ .../Handlers/BuildCreateHandlerTests.cs | 201 ++++++ .../BuildCreateVersionBucketHandlerTests.cs | 164 +++++ ...BuildCreateVersionContainerHandlerTests.cs | 136 ++++ ...uildCreateVersionFileUploadHandlerTests.cs | 284 ++++++++ .../BuildCreateVersionHandlerTests.cs | 70 ++ .../Handlers/BuildDeleteHandlerTests.cs | 130 ++++ .../Handlers/BuildGetHandlerTests.cs | 127 ++++ .../Handlers/BuildInstallsHandlerTests.cs | 103 +++ .../Handlers/BuildListHandlerTests.cs | 95 +++ .../Handlers/BuildUpdateHandlerTests.cs | 179 +++++ .../Handlers/FleetCreateHandlerTests.cs | 178 +++++ .../Handlers/FleetDeleteHandlerTests.cs | 125 ++++ .../Handlers/FleetGetHandlerTests.cs | 157 +++++ .../Handlers/FleetListHandlerTests.cs | 94 +++ .../Handlers/FleetRegionCreateHandlerTests.cs | 110 +++ .../Handlers/FleetRegionUpdateHandlerTests.cs | 142 ++++ .../Handlers/FleetUpdateHandlerTests.cs | 230 +++++++ .../Handlers/HandlerCommon.cs | 101 +++ .../Handlers/RegionAvailableHandlerTests.cs | 99 +++ .../Handlers/RegionTemplatesHandlerTests.cs | 95 +++ .../Handlers/ServerGetHandlerTests.cs | 156 +++++ .../Handlers/ServerListHandlerTests.cs | 134 ++++ .../Input/BuildConfigurationIdInputTests.cs | 14 + .../Input/BuildConfigurationListInputTests.cs | 19 + .../Input/BuildCreateInputTests.cs | 47 ++ .../Input/BuildIdInputTests.cs | 14 + .../Input/FleetCreateInputTests.cs | 39 ++ .../Input/FleetIdInputTests.cs | 14 + .../Input/ServerIdInputTests.cs | 14 + .../Input/ServerListInputTests.cs | 43 ++ .../GameServerHostingBuildApiV1AsyncMock.cs | 502 ++++++++++++++ ...rverHostingBuildConfigurationsApiV1Mock.cs | 165 +++++ .../Mocks/GameServerHostingFleetsApiV1Mock.cs | 372 ++++++++++ .../GameServerHostingServersApiV1Mock.cs | 148 ++++ .../Model/BuildConfigurationOutputTests.cs | 74 ++ ...uildInstallsItemFailuresItemOutputTests.cs | 44 ++ .../BuildInstallsItemFailuresOutputTests.cs | 58 ++ .../Model/BuildInstallsItemOutputTests.cs | 100 +++ ...BuildInstallsItemRegionsItemOutputTests.cs | 47 ++ .../BuildInstallsItemRegionsOutputTests.cs | 56 ++ .../Model/BuildInstallsOutputTests.cs | 160 +++++ .../Model/BuildListOutputTests.cs | 96 +++ .../Model/BuildOutputTests.cs | 99 +++ .../Model/CcdOutputTests.cs | 42 ++ .../Model/FleetGetOutputTests.cs | 90 +++ .../Model/FleetListItemOutputTests.cs | 77 +++ .../Model/FleetListOutputTests.cs | 98 +++ .../Model/FleetRegionCreateOutputTests.cs | 51 ++ .../Model/FleetRegionUpdateOutputTests.cs | 64 ++ .../Model/RegionListOutputTests.cs | 54 ++ .../Model/RegionOutputTests.cs | 41 ++ .../Model/RegionTemplateListOutputTests.cs | 41 ++ .../Model/ServerGetOutputTests.cs | 81 +++ .../Model/ServersItemOutputTests.cs | 75 ++ .../Model/ServersOutputTests.cs | 86 +++ .../Service/GameServerHostingServiceTests.cs | 68 ++ .../Services/ApiClientFactoryTests.cs | 116 ++++ .../Services/BuildClientTests.cs | 133 ++++ .../Services/BuildConfigsClientTests.cs | 134 ++++ .../Services/CcdCloudStorageTests.cs | 125 ++++ .../Services/CcdHashExtensionsTests.cs | 17 + .../Services/DeployFileServiceTests.cs | 91 +++ .../Services/DummyBinaryBuilderTests.cs | 24 + .../Services/FileReaderAdapterTests.cs | 35 + .../Services/FleetsClientTests.cs | 266 ++++++++ .../GameServerHostingConfigLoaderTests.cs | 135 ++++ ...ices.Cli.GameServerHosting.UnitTest.csproj | 24 + .../Usings.cs | 2 + .../CloudContentDeliveryEndpoints.cs | 10 + .../Exceptions/DuplicateResourceException.cs | 15 + .../Exceptions/InvalidConfigException.cs | 13 + .../Exceptions/InvalidExtensionException.cs | 13 + .../InvalidKeyValuePairException.cs | 12 + .../Exceptions/InvalidResponseException.cs | 12 + .../Exceptions/MissingInputException.cs | 12 + .../Exceptions/PathNotFoundException.cs | 13 + .../Exceptions/SyncFailedException.cs | 15 + .../GameServerHostingFeatures.cs | 8 + .../GameServerHostingModule.cs | 645 ++++++++++++++++++ .../BuildConfigurationCreateHandler.cs | 81 +++ .../BuildConfigurationDeleteHandler.cs | 48 ++ .../Handlers/BuildConfigurationGetHandler.cs | 50 ++ .../Handlers/BuildConfigurationListHandler.cs | 61 ++ .../BuildConfigurationUpdateHandler.cs | 105 +++ .../Handlers/BuildCreateHandler.cs | 52 ++ .../BuildCreateVersionBucketHandler.cs | 48 ++ .../BuildCreateVersionContainerHandler.cs | 42 ++ .../BuildCreateVersionFileUploadHandler.cs | 280 ++++++++ .../Handlers/BuildCreateVersionHandler.cs | 94 +++ .../Handlers/BuildDeleteHandler.cs | 48 ++ .../Handlers/BuildGetHandler.cs | 50 ++ .../Handlers/BuildInstallsHandler.cs | 50 ++ .../Handlers/BuildListHandler.cs | 46 ++ .../Handlers/BuildUpdateHandler.cs | 50 ++ .../Handlers/FleetCreateHandler.cs | 63 ++ .../Handlers/FleetDeleteHandler.cs | 48 ++ .../Handlers/FleetGetHandler.cs | 49 ++ .../Handlers/FleetListHandler.cs | 46 ++ .../Handlers/FleetRegionCreateHandler.cs | 59 ++ .../Handlers/FleetRegionUpdateHandler.cs | 67 ++ .../Handlers/FleetUpdateHandler.cs | 67 ++ .../Handlers/RegionAvailableHandler.cs | 50 ++ .../Handlers/RegionTemplatesHandler.cs | 46 ++ .../Handlers/ServerGetHandler.cs | 49 ++ .../Handlers/ServerListHandler.cs | 76 +++ .../Input/BuildConfigurationCreateInput.cs | 94 +++ .../Input/BuildConfigurationIdInput.cs | 33 + .../Input/BuildConfigurationListInput.cs | 47 ++ .../Input/BuildConfigurationUpdateInput.cs | 14 + .../Input/BuildCreateInput.cs | 67 ++ .../Input/BuildCreateVersionInput.cs | 64 ++ .../Input/BuildIdInput.cs | 30 + .../Input/BuildUpdateInput.cs | 17 + .../Input/FleetCreateInput.cs | 94 +++ .../Input/FleetIdInput.cs | 30 + .../Input/FleetRegionCreateInput.cs | 57 ++ .../Input/FleetRegionUpdateInput.cs | 104 +++ .../Input/FleetUpdateInput.cs | 53 ++ .../Input/ServerIdInput.cs | 30 + .../Input/ServerListInput.cs | 95 +++ .../Model/BuildConfigurationListItemOutput.cs | 53 ++ .../Model/BuildConfigurationListOutput.cs | 22 + .../Model/BuildConfigurationOutput.cs | 57 ++ .../BuildInstallsItemFailuresItemOutput.cs | 31 + .../Model/BuildInstallsItemFailuresOutput.cs | 22 + .../Model/BuildInstallsItemOutput.cs | 46 ++ .../BuildInstallsItemRegionsItemOutput.cs | 30 + .../Model/BuildInstallsItemRegionsOutput.cs | 22 + .../Model/BuildInstallsOutput.cs | 22 + .../Model/BuildListOutput.cs | 22 + .../Model/BuildOutput.cs | 70 ++ .../Model/CcdOutput.cs | 28 + .../Model/FleetGetOutput.cs | 62 ++ .../Model/FleetListItemOutput.cs | 42 ++ .../Model/FleetListOutput.cs | 22 + .../Model/FleetRegionCreateOutput.cs | 36 + .../Model/FleetRegionUpdateOutput.cs | 48 ++ .../Model/LocalFile.cs | 23 + .../Model/RegionTemplateListItemOutput.cs | 27 + .../Model/RegionTemplateListOutput.cs | 22 + .../Model/ServerGetOutput.cs | 65 ++ .../Model/ServersItemOutput.cs | 57 ++ .../Model/ServersOutput.cs | 22 + .../Service/GameServerHostingService.cs | 42 ++ .../Service/IGameServerHostingService.cs | 13 + .../Services/ApiClientFactory.cs | 72 ++ .../Services/BuildClient.cs | 71 ++ .../Services/BuildConfigsClient.cs | 79 +++ .../Services/CcdCloudStorageClient.cs | 127 ++++ .../Services/DeployFileService.cs | 50 ++ .../Services/DummyBinaryBuilder.cs | 17 + .../Services/FileReaderAdapter.cs | 21 + .../Services/FleetClient.cs | 102 +++ .../Services/GameServerHostingApiConfig.cs | 8 + .../Services/GameServerHostingConfigLoader.cs | 87 +++ .../Services/IDeployFileService.cs | 7 + .../IGameServerHostingConfigLoader.cs | 8 + .../Services/ResourceNameTypeConverter.cs | 36 + ...nity.Services.Cli.GameServerHosting.csproj | 34 + .../GameServerHostingApiMock.cs | 289 +++++++- .../ServiceMocks/GameServerHosting/Keys.cs | 14 + ...verHostingBuildConfigurationCreateTests.cs | 182 +++++ ...verHostingBuildConfigurationDeleteTests.cs | 86 +++ ...ServerHostingBuildConfigurationGetTests.cs | 104 +++ ...erverHostingBuildConfigurationListTests.cs | 78 +++ ...verHostingBuildConfigurationUpdateTests.cs | 182 +++++ .../GameServerHostingBuildCreateTests.cs | 142 ++++ ...ameServerHostingBuildCreateVersionTests.cs | 113 +++ .../GameServerHostingBuildDeleteTests.cs | 99 +++ .../GameServerHostingBuildGetTests.cs | 104 +++ .../GameServerHostingBuildInstallsTests.cs | 104 +++ .../GameServerHostingBuildListTests.cs | 79 +++ .../GameServerHostingBuildUpdateTests.cs | 73 ++ ...eServerHostingFleetRegionAvailableTests.cs | 90 +++ ...GameServerHostingFleetRegionCreateTests.cs | 185 +++++ ...eServerHostingFleetRegionTemplatesTests.cs | 77 +++ .../GameServerHostingFleetTests.cs | 512 ++++++++++++++ .../GameServerHostingServerGetTests.cs | 105 +++ .../GameServerHostingServerListTest.cs | 121 ++++ .../GameServerHostingTests.cs | 68 ++ .../Unity.Services.Cli.IntegrationTest.csproj | 1 + .../Handlers/CreateLeaderboardHandlerTests.cs | 4 +- .../Handlers/DeleteLeaderboardHandlerTests.cs | 4 +- .../Handlers/ExportHandlerTests.cs | 6 +- .../GetLeaderboardConfigsHandlerTests.cs | 4 +- .../Handlers/GetLeaderboardHandlerTests.cs | 6 +- .../Handlers/ImportHandlerTests.cs | 16 +- .../Handlers/ResetLeaderboardHandlerTests.cs | 4 +- .../Handlers/UpdateLeaderboardHandlerTests.cs | 4 +- .../Handlers/CreateLeaderboardHandler.cs | 2 +- .../Handlers/DeleteLeaderboardHandler.cs | 2 +- .../Handlers/GetLeaderboardConfigsHandler.cs | 4 +- .../Handlers/GetLeaderboardHandler.cs | 2 +- .../Handlers/ResetLeaderboardHandler.cs | 2 +- .../Handlers/UpdateLeaderboardHandler.cs | 2 +- .../Handlers/ExportHandlerTests.cs | 5 +- .../Handlers/ImportHandlerTests.cs | 7 +- .../Handlers/BulkUpdateLobbyHandler.cs | 2 +- .../Handlers/ConfigGetHandler.cs | 2 +- .../Handlers/CreateLobbyHandler.cs | 2 +- .../Handlers/DeleteLobbyHandler.cs | 2 +- .../Handlers/GetHostedLobbiesHandler.cs | 2 +- .../Handlers/GetJoinedLobbiesHandler.cs | 2 +- .../Handlers/GetLobbyHandler.cs | 2 +- .../Handlers/HeartbeatHandler.cs | 2 +- .../Handlers/JoinLobbyHandler.cs | 2 +- .../Handlers/QueryLobbiesHandler.cs | 2 +- .../Handlers/QuickJoinHandler.cs | 2 +- .../Handlers/ReconnectHandler.cs | 2 +- .../Handlers/RemovePlayerHandler.cs | 2 +- .../Handlers/RequestTokenHandler.cs | 2 +- .../Handlers/UpdateLobbyHandler.cs | 2 +- .../Handlers/UpdatePlayerHandler.cs | 2 +- .../Deploy/RemoteConfigFetchServiceTests.cs | 8 +- .../ImportExport/ExportHandlerTests.cs | 4 +- .../ImportExport/ImportHandlerTests.cs | 6 +- .../Deploy/RemoteConfigFetchService.cs | 6 +- .../Unity.Services.Cli.RemoteConfig.csproj | 2 +- .../Authenticator/AuthenticatorV1Tests.cs | 15 + .../Authenticator/AuthenticatorV1.cs | 5 + Unity.Services.Cli/Unity.Services.Cli.sln | 10 + .../Unity.Services.Cli/Program.cs | 16 +- .../Properties/launchSettings.json | 2 +- .../Unity.Services.Cli.csproj | 3 +- 286 files changed, 16226 insertions(+), 193 deletions(-) create mode 100644 Dockerfile create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/GameServerHostingModuleTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/GameServerHostingUnitTestsConstants.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationCreateHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationDeleteHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationGetHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationListHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationUpdateHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionBucketHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionContainerHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionFileUploadHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildDeleteHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildGetHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildInstallsHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildListHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildUpdateHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetCreateHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetDeleteHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetGetHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetListHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetRegionCreateHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetRegionUpdateHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetUpdateHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/HandlerCommon.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/RegionAvailableHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/RegionTemplatesHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/ServerGetHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/ServerListHandlerTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildConfigurationIdInputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildConfigurationListInputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildCreateInputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildIdInputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/FleetCreateInputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/FleetIdInputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/ServerIdInputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/ServerListInputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingBuildApiV1AsyncMock.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingBuildConfigurationsApiV1Mock.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingFleetsApiV1Mock.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingServersApiV1Mock.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildConfigurationOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemFailuresItemOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemFailuresOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemRegionsItemOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemRegionsOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildListOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/CcdOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetGetOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetListItemOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetListOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetRegionCreateOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetRegionUpdateOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/RegionListOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/RegionOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/RegionTemplateListOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/ServerGetOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/ServersItemOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/ServersOutputTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Service/GameServerHostingServiceTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/ApiClientFactoryTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/BuildClientTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/BuildConfigsClientTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/CcdCloudStorageTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/CcdHashExtensionsTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/DeployFileServiceTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/DummyBinaryBuilderTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/FileReaderAdapterTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/FleetsClientTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/GameServerHostingConfigLoaderTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Unity.Services.Cli.GameServerHosting.UnitTest.csproj create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Usings.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Endpoints/CloudContentDeliveryEndpoints.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/DuplicateResourceException.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidConfigException.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidExtensionException.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidKeyValuePairException.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidResponseException.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/MissingInputException.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/PathNotFoundException.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/SyncFailedException.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/GameServerHostingFeatures.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/GameServerHostingModule.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationCreateHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationDeleteHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationGetHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationListHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationUpdateHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionBucketHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionContainerHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionFileUploadHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildDeleteHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildGetHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildInstallsHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildListHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildUpdateHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetCreateHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetDeleteHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetGetHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetListHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetRegionCreateHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetRegionUpdateHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetUpdateHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/RegionAvailableHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/RegionTemplatesHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/ServerGetHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/ServerListHandler.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationCreateInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationIdInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationListInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationUpdateInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildCreateInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildCreateVersionInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildIdInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildUpdateInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetCreateInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetIdInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetRegionCreateInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetRegionUpdateInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetUpdateInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/ServerIdInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/ServerListInput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildConfigurationListItemOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildConfigurationListOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildConfigurationOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemFailuresItemOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemFailuresOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemRegionsItemOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemRegionsOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildListOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/CcdOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetGetOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetListItemOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetListOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetRegionCreateOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetRegionUpdateOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/LocalFile.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/RegionTemplateListItemOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/RegionTemplateListOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/ServerGetOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/ServersItemOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/ServersOutput.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Service/GameServerHostingService.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Service/IGameServerHostingService.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/ApiClientFactory.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/BuildClient.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/BuildConfigsClient.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/CcdCloudStorageClient.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/DeployFileService.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/DummyBinaryBuilder.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/FileReaderAdapter.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/FleetClient.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/GameServerHostingApiConfig.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/GameServerHostingConfigLoader.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/IDeployFileService.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/IGameServerHostingConfigLoader.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/ResourceNameTypeConverter.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Unity.Services.Cli.GameServerHosting.csproj create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationCreateTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationDeleteTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationGetTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationListTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationUpdateTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildCreateTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildCreateVersionTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildDeleteTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildGetTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildInstallsTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildListTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildUpdateTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetRegionAvailableTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetRegionCreateTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetRegionTemplatesTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingServerGetTests.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingServerListTest.cs create mode 100644 Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e7fafc..144e899 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,17 @@ All notable changes to UGS CLI will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -# [1.0.0-beta.5] - 2023-06-28 +## [1.0.0-beta.6] - 2023-07-10 + +### Added +- Game Server Hosting Module Service commands. Run `ugs gsh -h` to show usage. + - Supports builds, build configurations, fleets, fleet regions and servers. + +### Fixed +- A bug with the login command when stdin is redirected. +- A bug preventing Remote Config fetch dry run to update the fetched file name. + +## [1.0.0-beta.5] - 2023-06-28 ### Added - Added Batching to import and deploy to help prevent "Too Many Requests" error. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1456186 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM mcr.microsoft.com/dotnet/runtime-deps:7.0.4-alpine3.16-amd64 + +ARG UGS_VERSION=latest + +RUN apk add --no-cache curl ncurses + +# Set the URL based on the version +RUN if [ "$UGS_VERSION" = "latest" ]; then \ + UGS_URL="https://github.com/Unity-Technologies/unity-gaming-services-cli/releases/latest/download/ugs-linux-musl-x64"; \ + else \ + UGS_URL="https://github.com/Unity-Technologies/unity-gaming-services-cli/releases/download/$UGS_VERSION/ugs-linux-musl-x64"; \ + fi \ + && echo "Installing UGS cli version \"$UGS_VERSION\" from \"$UGS_URL\"" \ + && curl -f -L "$UGS_URL" -o /bin/ugs + +RUN chmod +x /bin/ugs + +# Add some color to it +ENV TERM=xterm-256color diff --git a/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/DeletePlayerPolicyStatementsHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/DeletePlayerPolicyStatementsHandlerTests.cs index 7c213a7..b2308d7 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/DeletePlayerPolicyStatementsHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/DeletePlayerPolicyStatementsHandlerTests.cs @@ -35,7 +35,7 @@ public async Task DeletePlayerPolicyStatementsAsync_CallsLoadingIndicator() await DeletePlayerPolicyStatementsHandler.DeletePlayerPolicyStatementsAsync(null!, null!, null!, null!, mockLoadingIndicator.Object, CancellationToken.None); mockLoadingIndicator.Verify(ex => ex - .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); } [Test] @@ -48,14 +48,14 @@ public async Task DeletePlayerPolicyStatementsHandler_valid() FilePath = new FileInfo(TestValues.FilePath) }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()).ReturnsAsync(TestValues.ValidEnvironmentId); + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(TestValues.ValidEnvironmentId); m_MockAccessService?.Setup(x => - x.DeletePlayerPolicyStatementsAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, TestValues.ValidPlayerId, It.IsAny(), CancellationToken.None)); + x.DeletePlayerPolicyStatementsAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, TestValues.ValidPlayerId, It.IsAny(), CancellationToken.None)); await DeletePlayerPolicyStatementsHandler.DeletePlayerPolicyStatementsAsync(input, m_MockUnityEnvironment.Object, m_MockAccessService!.Object, m_MockLogger!.Object, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockAccessService.Verify(x => x.DeletePlayerPolicyStatementsAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, TestValues.ValidPlayerId, It.IsAny(), CancellationToken.None), Times.Once); TestsHelper.VerifyLoggerWasCalled(m_MockLogger, LogLevel.Information, expectedTimes: Times.Once); } diff --git a/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/DeleteProjectPolicyStatementsHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/DeleteProjectPolicyStatementsHandlerTests.cs index 114a92d..d0836cd 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/DeleteProjectPolicyStatementsHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/DeleteProjectPolicyStatementsHandlerTests.cs @@ -35,7 +35,7 @@ public async Task DeleteProjectPolicyStatementsAsync_CallsLoadingIndicator() await DeleteProjectPolicyStatementsHandler.DeleteProjectPolicyStatementsAsync(null!, null!, null!, null!, mockLoadingIndicator.Object, CancellationToken.None); mockLoadingIndicator.Verify(ex => ex - .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); } [Test] @@ -47,14 +47,14 @@ public async Task DeleteProjectPolicyStatementsHandler_valid() FilePath = new FileInfo(TestValues.FilePath) }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()).ReturnsAsync(TestValues.ValidEnvironmentId); + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(TestValues.ValidEnvironmentId); m_MockAccessService?.Setup(x => - x.DeletePolicyStatementsAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, It.IsAny(), CancellationToken.None)); + x.DeletePolicyStatementsAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, It.IsAny(), CancellationToken.None)); await DeleteProjectPolicyStatementsHandler.DeleteProjectPolicyStatementsAsync(input, m_MockUnityEnvironment.Object, m_MockAccessService!.Object, m_MockLogger!.Object, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockAccessService.Verify(x => x.DeletePolicyStatementsAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, It.IsAny(), CancellationToken.None), Times.Once); TestsHelper.VerifyLoggerWasCalled(m_MockLogger, LogLevel.Information, expectedTimes: Times.Once); } diff --git a/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/GetAllPlayerPoliciesHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/GetAllPlayerPoliciesHandlerTests.cs index 17f5222..d56b72f 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/GetAllPlayerPoliciesHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/GetAllPlayerPoliciesHandlerTests.cs @@ -46,14 +46,14 @@ public async Task GetAllPlayerPoliciesHandler_valid() CloudProjectId = TestValues.ValidProjectId, }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()).ReturnsAsync(TestValues.ValidEnvironmentId); + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(TestValues.ValidEnvironmentId); m_MockAccessService?.Setup(x => x.GetAllPlayerPoliciesAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, CancellationToken.None)) .ReturnsAsync(It.IsAny>()); await GetAllPlayerPoliciesHandler.GetAllPlayerPoliciesAsync(input, m_MockUnityEnvironment.Object, m_MockAccessService!.Object, m_MockLogger!.Object, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockAccessService.Verify(x => x.GetAllPlayerPoliciesAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, CancellationToken.None), Times.Once); TestsHelper.VerifyLoggerWasCalled( m_MockLogger, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Once); diff --git a/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/GetPlayerPolicyHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/GetPlayerPolicyHandlerTests.cs index 142963d..5a28290 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/GetPlayerPolicyHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/GetPlayerPolicyHandlerTests.cs @@ -48,14 +48,14 @@ public async Task GetPlayerPolicyHandler_valid() PlayerId = TestValues.ValidPlayerId, }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()).ReturnsAsync(TestValues.ValidEnvironmentId); + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(TestValues.ValidEnvironmentId); m_MockAccessService?.Setup(x => x.GetPlayerPolicyAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, TestValues.ValidPlayerId, CancellationToken.None)) .ReturnsAsync(It.IsAny()); await GetPlayerPolicyHandler.GetPlayerPolicyAsync(input, m_MockUnityEnvironment.Object, m_MockAccessService!.Object, m_MockLogger!.Object, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockAccessService.Verify(x => x.GetPlayerPolicyAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, TestValues.ValidPlayerId, CancellationToken.None), Times.Once); TestsHelper.VerifyLoggerWasCalled( m_MockLogger, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Once); diff --git a/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/GetProjectPolicyHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/GetProjectPolicyHandlerTests.cs index 03a459e..e565e93 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/GetProjectPolicyHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/GetProjectPolicyHandlerTests.cs @@ -47,14 +47,14 @@ public async Task GetProjectPolicyHandler_valid() CloudProjectId = TestValues.ValidProjectId, }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()).ReturnsAsync(TestValues.ValidEnvironmentId); + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(TestValues.ValidEnvironmentId); m_MockAccessService?.Setup(x => x.GetPolicyAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, CancellationToken.None)) .ReturnsAsync(It.IsAny()); await GetProjectPolicyHandler.GetProjectPolicyAsync(input, m_MockUnityEnvironment.Object, m_MockAccessService!.Object, m_MockLogger!.Object, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockAccessService.Verify(x => x.GetPolicyAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, CancellationToken.None), Times.Once); TestsHelper.VerifyLoggerWasCalled( m_MockLogger, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Once); diff --git a/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/UpsertPlayerPolicyHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/UpsertPlayerPolicyHandlerTests.cs index 6b7c7b4..9d9c4e1 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/UpsertPlayerPolicyHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/UpsertPlayerPolicyHandlerTests.cs @@ -35,7 +35,7 @@ public async Task UpsertPlayerPolicyAsync_CallsLoadingIndicator() await UpsertPlayerPolicyHandler.UpsertPlayerPolicyAsync(null!, null!, null!, null!, mockLoadingIndicator.Object, CancellationToken.None); mockLoadingIndicator.Verify(ex => ex - .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); } [Test] @@ -48,14 +48,14 @@ public async Task UpsertPlayerPolicyHandler_valid() PlayerId = TestValues.ValidPlayerId }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()).ReturnsAsync(TestValues.ValidEnvironmentId); + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(TestValues.ValidEnvironmentId); m_MockAccessService?.Setup(x => - x.UpsertPlayerPolicyAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, TestValues.ValidPlayerId, It.IsAny(), CancellationToken.None)); + x.UpsertPlayerPolicyAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, TestValues.ValidPlayerId, It.IsAny(), CancellationToken.None)); await UpsertPlayerPolicyHandler.UpsertPlayerPolicyAsync(input, m_MockUnityEnvironment.Object, m_MockAccessService!.Object, m_MockLogger!.Object, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockAccessService.Verify(x => x.UpsertPlayerPolicyAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, TestValues.ValidPlayerId, It.IsAny(), CancellationToken.None), Times.Once); TestsHelper.VerifyLoggerWasCalled(m_MockLogger, LogLevel.Information, expectedTimes: Times.Once); } diff --git a/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/UpsertProjectPolicyHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/UpsertProjectPolicyHandlerTests.cs index a66b486..a975648 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/UpsertProjectPolicyHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Access.UnitTest/Handlers/UpsertProjectPolicyHandlerTests.cs @@ -35,7 +35,7 @@ public async Task UpsertProjectPolicyAsync_CallsLoadingIndicator() await UpsertProjectPolicyHandler.UpsertProjectPolicyAsync(null!, null!, null!, null!, mockLoadingIndicator.Object, CancellationToken.None); mockLoadingIndicator.Verify(ex => ex - .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); } [Test] @@ -47,14 +47,14 @@ public async Task UpsertProjectPolicyHandler_valid() FilePath = new FileInfo(TestValues.FilePath) }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()).ReturnsAsync(TestValues.ValidEnvironmentId); + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(TestValues.ValidEnvironmentId); m_MockAccessService?.Setup(x => - x.UpsertPolicyAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, It.IsAny(), CancellationToken.None)); + x.UpsertPolicyAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, It.IsAny(), CancellationToken.None)); await UpsertProjectPolicyHandler.UpsertProjectPolicyAsync(input, m_MockUnityEnvironment.Object, m_MockAccessService!.Object, m_MockLogger!.Object, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockAccessService.Verify(x => x.UpsertPolicyAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, It.IsAny(), CancellationToken.None), Times.Once); TestsHelper.VerifyLoggerWasCalled(m_MockLogger, LogLevel.Information, expectedTimes: Times.Once); } diff --git a/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/DeletePlayerPolicyStatementsHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/DeletePlayerPolicyStatementsHandler.cs index abb59a4..11b9ab3 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/DeletePlayerPolicyStatementsHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/DeletePlayerPolicyStatementsHandler.cs @@ -19,7 +19,7 @@ internal static async Task DeletePlayerPolicyStatementsAsync(AccessInput input, IAccessService accessService, ILogger logger, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; var playerId = input.PlayerId!; var filePath = input.FilePath!; diff --git a/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/DeleteProjectPolicyStatementsHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/DeleteProjectPolicyStatementsHandler.cs index 35787cf..fb7cdda 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/DeleteProjectPolicyStatementsHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/DeleteProjectPolicyStatementsHandler.cs @@ -19,7 +19,7 @@ internal static async Task DeleteProjectPolicyStatementsAsync(AccessInput input, IAccessService accessService, ILogger logger, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; var filePath = input.FilePath!; diff --git a/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/GetAllPlayerPoliciesHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/GetAllPlayerPoliciesHandler.cs index 80f764f..58c9272 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/GetAllPlayerPoliciesHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/GetAllPlayerPoliciesHandler.cs @@ -21,7 +21,7 @@ internal static async Task GetAllPlayerPoliciesAsync(CommonInput input, IUnityEn IAccessService accessService, ILogger logger, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; var playerPolicies = await accessService.GetAllPlayerPoliciesAsync(projectId, environmentId, cancellationToken); diff --git a/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/GetPlayerPolicyHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/GetPlayerPolicyHandler.cs index 8c4f368..76d2c7a 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/GetPlayerPolicyHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/GetPlayerPolicyHandler.cs @@ -21,7 +21,7 @@ internal static async Task GetPlayerPolicyAsync(AccessInput input, IUnityEnviron IAccessService accessService, ILogger logger, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; var playerId = input.PlayerId!; diff --git a/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/GetProjectPolicyHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/GetProjectPolicyHandler.cs index 7257eb8..860c445 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/GetProjectPolicyHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/GetProjectPolicyHandler.cs @@ -21,7 +21,7 @@ internal static async Task GetProjectPolicyAsync(CommonInput input, IUnityEnviro IAccessService accessService, ILogger logger, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; var policy = await accessService.GetPolicyAsync(projectId, environmentId, cancellationToken); diff --git a/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/UpsertPlayerPolicyHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/UpsertPlayerPolicyHandler.cs index 112e19d..98a554d 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/UpsertPlayerPolicyHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/UpsertPlayerPolicyHandler.cs @@ -19,7 +19,7 @@ internal static async Task UpsertPlayerPolicyAsync(AccessInput input, IUnityEnvi IAccessService accessService, ILogger logger, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; var filePath = input.FilePath!; var playerId = input.PlayerId!; diff --git a/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/UpsertProjectPolicyHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/UpsertProjectPolicyHandler.cs index 389f0b6..452fe09 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/UpsertProjectPolicyHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Access/Handlers/UpsertProjectPolicyHandler.cs @@ -19,7 +19,7 @@ internal static async Task UpsertProjectPolicyAsync(AccessInput input, IUnityEnv IAccessService accessService, ILogger logger, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; var filePath = input.FilePath!; diff --git a/Unity.Services.Cli/Unity.Services.Cli.Authoring.UnitTest/Handlers/DeployHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Authoring.UnitTest/Handlers/DeployHandlerTests.cs index d2ce36f..bf3ce21 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Authoring.UnitTest/Handlers/DeployHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Authoring.UnitTest/Handlers/DeployHandlerTests.cs @@ -141,7 +141,7 @@ public void SetUp() m_Host.Setup(x => x.Services) .Returns(provider); - m_UnityEnvironment.Setup(x => x.FetchIdentifierAsync()).Returns(Task.FromResult(ValidEnvironmentId)); + m_UnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)).Returns(Task.FromResult(ValidEnvironmentId)); m_DeployFileService.Setup(x => x.ListFilesToDeploy(new[] { "" diff --git a/Unity.Services.Cli/Unity.Services.Cli.Authoring/Export/BaseExporter.cs b/Unity.Services.Cli/Unity.Services.Cli.Authoring/Export/BaseExporter.cs index b6fd0bf..55ee3ae 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Authoring/Export/BaseExporter.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Authoring/Export/BaseExporter.cs @@ -46,7 +46,7 @@ protected BaseExporter( public async Task ExportAsync(ExportInput input, CancellationToken cancellationToken) { var projectId = input.CloudProjectId!; - var environmentId = await m_UnityEnvironment.FetchIdentifierAsync(); + var environmentId = await m_UnityEnvironment.FetchIdentifierAsync(cancellationToken); var fileName = ImportExportUtils.ResolveFileName(input.FileName, FileName); var configs = await ListConfigsAsync(projectId, environmentId, cancellationToken); @@ -57,7 +57,7 @@ public async Task ExportAsync(ExportInput input, CancellationToken cancellationT await ExportToZipAsync(input.OutputDirectory, fileName, state, cancellationToken); } - m_Logger.LogResultValue(new ImportExportResult( state.ExportedItems().ToList()) + m_Logger.LogResultValue(new ImportExportResult(state.ExportedItems().ToList()) { Header = input.DryRun ? "The following items will be exported:" : "The following items were exported:", DryRun = input.DryRun diff --git a/Unity.Services.Cli/Unity.Services.Cli.Authoring/Handlers/DeployHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Authoring/Handlers/DeployHandler.cs index 114fae0..a02ff61 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Authoring/Handlers/DeployHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Authoring/Handlers/DeployHandler.cs @@ -72,7 +72,7 @@ CancellationToken cancellationToken return; } - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; var deploymentServices = services .Where(s => CheckService(input, s)) diff --git a/Unity.Services.Cli/Unity.Services.Cli.Authoring/Import/BaseImporter.cs b/Unity.Services.Cli/Unity.Services.Cli.Authoring/Import/BaseImporter.cs index 6f08fc6..cde9d8b 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Authoring/Import/BaseImporter.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Authoring/Import/BaseImporter.cs @@ -50,7 +50,7 @@ protected BaseImporter( /// public async Task ImportAsync(ImportInput input, CancellationToken cancellationToken, int maxParallelTaskLimit = 10) { - var environmentId = await m_UnityEnvironment.FetchIdentifierAsync(); + var environmentId = await m_UnityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input!.CloudProjectId!; var fileName = ImportExportUtils.ResolveFileName(input.FileName, FileName); diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Authoring/Fetch/JavaScriptFetchServiceTests.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Authoring/Fetch/JavaScriptFetchServiceTests.cs index b67d044..09d5dff 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Authoring/Fetch/JavaScriptFetchServiceTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Authoring/Fetch/JavaScriptFetchServiceTests.cs @@ -66,7 +66,7 @@ public async Task FetchAsyncInitializesClientAndGetsResultFromHandler(bool dryRu input.CloudProjectId = TestValues.ValidProjectId; input.DryRun = dryRun; input.Reconcile = reconcile; - m_UnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_UnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); var expectedResult = new FetchResult( Array.Empty(), diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/CreateHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/CreateHandlerTests.cs index e8d5daa..25c8e02 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/CreateHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/CreateHandlerTests.cs @@ -64,7 +64,7 @@ public async Task CreateAsync_CallsCreateAsyncWhenInputIsValid() ScriptLanguage = k_ValidScriptLanguage, ScriptName = TestValues.ValidScriptName, }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); m_MockInputParseService.Setup(x => x.ParseScriptType(input)) .Returns(ScriptType.API); @@ -85,7 +85,7 @@ await CreateHandler.CreateAsync( (StatusContext)null!, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockInputParseService.Verify(x => x.ParseScriptType(input), Times.Once); m_MockInputParseService.Verify(x => x.ParseLanguage(input), Times.Once); m_MockInputParseService.Verify(x => x.LoadScriptCodeAsync(input, CancellationToken.None), Times.Once); diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/DeleteHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/DeleteHandlerTests.cs index e16f1fc..dbb145b 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/DeleteHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/DeleteHandlerTests.cs @@ -50,7 +50,7 @@ public async Task DeleteAsync_CallsDeleteServiceAndLoggerWhenInputIsValid() CloudProjectId = TestValues.ValidProjectId, ScriptName = TestValues.ValidScriptName }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); m_MockCloudCode.Setup( ex => ex.DeleteAsync( @@ -65,7 +65,7 @@ await DeleteHandler.DeleteAsync( CancellationToken.None ); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockCloudCode.Verify( ex => ex.DeleteAsync( TestValues.ValidProjectId, TestValues.ValidEnvironmentId, TestValues.ValidScriptName, CancellationToken.None), diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ExportModulesHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ExportModulesHandlerTests.cs index b0c2596..33e82f6 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ExportModulesHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ExportModulesHandlerTests.cs @@ -98,7 +98,7 @@ public async Task ExportAsync_ExportsAndZips() OutputDirectory = "test_output_directory" }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); m_MockCloudCodeService.Setup( diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ExportScriptsHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ExportScriptsHandlerTests.cs index 4b549b3..2907ef4 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ExportScriptsHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ExportScriptsHandlerTests.cs @@ -112,7 +112,7 @@ public async Task ExportAsync_ExportsAndZips() x.File.Exists(It.IsAny())) .Returns(false); - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); m_MockCloudCodeService.Setup( diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/GetHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/GetHandlerTests.cs index 59d0828..b3d4ba6 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/GetHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/GetHandlerTests.cs @@ -66,7 +66,7 @@ public async Task GetHandler_ValidInputLogsResult() CloudProjectId = TestValues.ValidProjectId, ScriptName = "test" }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await GetHandler.GetAsync( @@ -77,7 +77,7 @@ await GetHandler.GetAsync( CancellationToken.None ); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockCloudCode.Verify( api => api.GetAsync( TestValues.ValidProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/GetModuleHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/GetModuleHandlerTests.cs index 5befd86..e4fc0b6 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/GetModuleHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/GetModuleHandlerTests.cs @@ -64,7 +64,7 @@ public async Task GetModuleHandler_ValidInputLogsResult() CloudProjectId = TestValues.ValidProjectId, ModuleName = "foo" }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await GetModuleHandler.GetModuleAsync( @@ -75,7 +75,7 @@ await GetModuleHandler.GetModuleAsync( CancellationToken.None ); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockCloudCode.Verify( api => api.GetModuleAsync( TestValues.ValidProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ImportModulesHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ImportModulesHandlerTests.cs index f231896..f3253a7 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ImportModulesHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ImportModulesHandlerTests.cs @@ -105,7 +105,7 @@ public void SetUp() za => za.GetEntry( It.IsAny(), It.IsAny())) - .Returns( new ZipEntryStream(mockStream) ); + .Returns(new ZipEntryStream(mockStream)); } [Test] @@ -136,7 +136,7 @@ public async Task ImportAsync_Unzips() DryRun = true }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await ImportInternalAsync(importInput); @@ -158,7 +158,7 @@ public async Task ImportAsync_DryRunDoesNotImport() DryRun = true }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await ImportInternalAsync(importInput); @@ -177,9 +177,9 @@ public void ThrowsWhenModulePathIsEmpty() InputDirectory = "mock_input_directory", }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); - SetupList(m_ModulesListSingleModuleResponse, new List(){m_MockModule}); + SetupList(m_ModulesListSingleModuleResponse, new List() { m_MockModule }); SetupCreateOrUpdate(); m_MockArchiver.Setup( @@ -211,9 +211,9 @@ public async Task ModuleExists_Updates() FileName = "test.ccmzip" }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); - SetupList(m_ModulesListSingleModuleResponse, new List(){m_MockModule}); + SetupList(m_ModulesListSingleModuleResponse, new List() { m_MockModule }); SetupCreateOrUpdate(); m_MockArchiver.Setup( @@ -244,9 +244,9 @@ public void ModuleUpdate_Fails() FileName = "test.ccmzip" }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); - SetupList(m_ModulesListSingleModuleResponse, new List(){m_MockModule}); + SetupList(m_ModulesListSingleModuleResponse, new List() { m_MockModule }); SetupCreateOrUpdate(true); m_MockArchiver.Setup( @@ -293,10 +293,10 @@ public async Task ConfigDoesNotExist_Creates() za => za.GetEntry( It.IsAny(), It.IsAny())) - .Returns( new ZipEntryStream(mockStream) ); + .Returns(new ZipEntryStream(mockStream)); - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); SetupGet(null!, true); SetupList(new List(), new List()); @@ -331,7 +331,7 @@ public void ConfigDoesNotExist_CreateFails() m_MockModule }); - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); SetupGet(null!, true); SetupList(new List(), new List()); @@ -356,7 +356,7 @@ public async Task Reconcile_Deletes() Reconcile = true }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); m_MockArchiver.Setup( za => za.UnzipAsync( @@ -371,11 +371,11 @@ public async Task Reconcile_Deletes() SetupList(new List() { new(m_MockNonDuplicateModule.Name.ToString(), Language.JS, new Dictionary(), "url", DateNow, DateNow) - }, new List(){m_MockNonDuplicateModule}); + }, new List() { m_MockNonDuplicateModule }); SetupDelete(); - SetupList(m_ModulesListSingleModuleResponse, new List(){m_MockModule}); + SetupList(m_ModulesListSingleModuleResponse, new List() { m_MockModule }); SetupCreateOrUpdate(); @@ -398,7 +398,7 @@ public void Reconcile_Delete_Throws() Reconcile = true }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); m_MockArchiver.Setup( za => za.UnzipAsync( @@ -413,11 +413,11 @@ public void Reconcile_Delete_Throws() SetupList(new List() { new(m_MockNonDuplicateModule.Name.ToString(), Language.JS, new Dictionary(), "url", DateNow, DateNow) - }, new List(){m_MockNonDuplicateModule}); + }, new List() { m_MockNonDuplicateModule }); SetupDelete(true); - SetupList(m_ModulesListSingleModuleResponse, new List(){m_MockModule}); + SetupList(m_ModulesListSingleModuleResponse, new List() { m_MockModule }); SetupCreateOrUpdate(); diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ImportScriptsHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ImportScriptsHandlerTests.cs index 5a4d6ba..3da9ae4 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ImportScriptsHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ImportScriptsHandlerTests.cs @@ -58,9 +58,9 @@ class ImportScriptsHandlerTests }; - readonly CloudCodeScript m_MockNonDuplicateScript = new (new ScriptName("test"), Language.JS, "", "{}", new List(), DateNow.ToString()); + readonly CloudCodeScript m_MockNonDuplicateScript = new(new ScriptName("test"), Language.JS, "", "{}", new List(), DateNow.ToString()); // This mock script updates the existing script in m_MockScripts if used within tests - readonly CloudCodeScript m_MockScript = new (new ScriptName("test1"), Language.JS, "", "{}", new List(), DateNow.ToString()); + readonly CloudCodeScript m_MockScript = new(new ScriptName("test1"), Language.JS, "", "{}", new List(), DateNow.ToString()); readonly List m_MockScripts = new() { @@ -122,7 +122,7 @@ public async Task ImportAsync_Unzips() DryRun = true }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await ImportInternalAsync(importInput); @@ -143,7 +143,7 @@ public async Task ImportAsync_DryRunDoesNotImport() DryRun = true }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await ImportInternalAsync(importInput); @@ -162,10 +162,10 @@ public async Task ScriptExists_Updates() InputDirectory = "mock_input_directory", }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); SetupList(m_ScriptsListSingleScriptResponse); - SetupGet(new List(){m_MockScript}); + SetupGet(new List() { m_MockScript }); SetupUpdate(); m_MockArchiver.Setup( @@ -173,7 +173,7 @@ public async Task ScriptExists_Updates() It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult>(new List(){m_MockScript})); + .Returns(Task.FromResult>(new List() { m_MockScript })); m_MockCloudCodeScriptParser.Setup(p => p.ParseScriptParametersAsync( It.Is(s => s == m_MockNonDuplicateScript.Body), It.IsAny())) @@ -203,9 +203,9 @@ public async Task ConfigDoesNotExist_Creates() It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult>(new List(){m_MockScript})); + .Returns(Task.FromResult>(new List() { m_MockScript })); - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); SetupGet(null!, true); SetupCreate(); @@ -232,14 +232,14 @@ public async Task Reconcile_Deletes() Reconcile = true }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); m_MockArchiver.Setup( za => za.UnzipAsync( It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult>(new List(){m_MockNonDuplicateScript})); + .Returns(Task.FromResult>(new List() { m_MockNonDuplicateScript })); m_MockCloudCodeScriptParser.Setup(p => p.ParseScriptParametersAsync( It.Is(s => s == m_MockNonDuplicateScript.Body), It.IsAny())) @@ -247,13 +247,13 @@ public async Task Reconcile_Deletes() Array.Empty()))); - SetupList(new List(){new(m_MockNonDuplicateScript.Name.ToString(), ScriptType.API, Gateway.CloudCodeApiV1.Generated.Model.Language.JS, true, DateNow, 1),}); - SetupGet(new List(){m_MockNonDuplicateScript}); + SetupList(new List() { new(m_MockNonDuplicateScript.Name.ToString(), ScriptType.API, Gateway.CloudCodeApiV1.Generated.Model.Language.JS, true, DateNow, 1), }); + SetupGet(new List() { m_MockNonDuplicateScript }); SetupDelete(); SetupList(m_ScriptsListSingleScriptResponse); - SetupGet(new List(){m_MockScript}); + SetupGet(new List() { m_MockScript }); SetupCreate(); @@ -278,14 +278,14 @@ public void SucceedsOnRepublish() Reconcile = true }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); m_MockArchiver.Setup( za => za.UnzipAsync( It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult>(new List(){m_MockNonDuplicateScript})); + .Returns(Task.FromResult>(new List() { m_MockNonDuplicateScript })); m_MockCloudCodeScriptParser .Setup(p => p.ParseScriptParametersAsync( It.Is(s => s == m_MockNonDuplicateScript.Body), @@ -294,7 +294,8 @@ public void SucceedsOnRepublish() Task.FromResult(new ParseScriptParametersResult(m_MockNonDuplicateScript.Parameters.Any(), Array.Empty()))); var error = JsonConvert.SerializeObject( - new { + new + { code = CloudCodeScriptsImporter.ScriptAlreadyActive }); @@ -315,7 +316,7 @@ public void SucceedsOnRepublish() SetupDelete(); SetupList(m_ScriptsListSingleScriptResponse); - SetupGet(new List(){m_MockScript}); + SetupGet(new List() { m_MockScript }); SetupCreate(); Assert.DoesNotThrowAsync(async () => await ImportInternalAsync(importInput)); diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ListHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ListHandlerTests.cs index a39d991..4c8688a 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ListHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ListHandlerTests.cs @@ -50,7 +50,7 @@ public async Task ListHandler_CallsListServiceAndLoggerWhenInputIsValid() CloudProjectId = TestValues.ValidProjectId, ScriptName = TestValues.ValidScriptName }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await ListHandler.ListAsync( @@ -61,7 +61,7 @@ await ListHandler.ListAsync( CancellationToken.None ); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockCloudCode.Verify( ex => ex.ListAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, CancellationToken.None), Times.Once); diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ListModuleHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ListModuleHandlerTests.cs index 268aa7d..211151e 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ListModuleHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/ListModuleHandlerTests.cs @@ -40,7 +40,7 @@ await ListModulesHandler.ListModulesAsync(null!, null!, null!, null!, mockLoadingIndicator.Object, CancellationToken.None); mockLoadingIndicator.Verify(ex => ex - .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); } [Test] @@ -59,7 +59,7 @@ await ListModulesHandler.ListModulesAsync( CancellationToken.None ); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockCloudCode.Verify(ex => ex .ListModulesAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/PublishHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/PublishHandlerTests.cs index dd7ade8..451206c 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/PublishHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/PublishHandlerTests.cs @@ -53,7 +53,7 @@ public async Task PublishHandler_CallsPublishServiceAndLoggerWhenInputIsValid() Version = version, }; var expectedResponse = new PublishScriptResponse(version); - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); m_MockCloudCode.Setup( x => x.PublishAsync( @@ -71,7 +71,7 @@ await PublishHandler.PublishAsync( m_MockLogger.Object, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockCloudCode.Verify( ex => ex.PublishAsync( TestValues.ValidProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/UpdateHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/UpdateHandlerTests.cs index d83f17e..15a5c9b 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/UpdateHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/Handlers/UpdateHandlerTests.cs @@ -61,7 +61,7 @@ public async Task UpdateAsync_CallsUpdateAsyncWhenInputIsValid() FilePath = TestValues.ValidFilepath, ScriptName = TestValues.ValidScriptName }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); m_MockInputParseService.Setup(x => x.LoadScriptCodeAsync(input, CancellationToken.None)) .ReturnsAsync(TestValues.ValidCode); @@ -79,7 +79,7 @@ await UpdateHandler.UpdateAsync( (StatusContext)null!, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockCloudCode.Verify( e => e.UpdateAsync( TestValues.ValidProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Authoring/Fetch/JavaScriptFetchService.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Authoring/Fetch/JavaScriptFetchService.cs index 9e713a4..78c53fc 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Authoring/Fetch/JavaScriptFetchService.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Authoring/Fetch/JavaScriptFetchService.cs @@ -52,7 +52,7 @@ public JavaScriptFetchService( public async Task FetchAsync( FetchInput input, StatusContext? loadingContext, CancellationToken cancellationToken) { - var environmentId = await m_UnityEnvironment.FetchIdentifierAsync(); + var environmentId = await m_UnityEnvironment.FetchIdentifierAsync(cancellationToken); m_Client.Initialize(environmentId, input.CloudProjectId!, cancellationToken); loadingContext?.Status($"Reading {ServiceType} files..."); diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/CreateHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/CreateHandler.cs index 28cbbd4..afc5253 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/CreateHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/CreateHandler.cs @@ -34,7 +34,7 @@ internal static async Task CreateAsync( StatusContext? loadingContext, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; loadingContext?.Status("Loading script..."); diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/DeleteHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/DeleteHandler.cs index e378eec..2608922 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/DeleteHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/DeleteHandler.cs @@ -28,7 +28,7 @@ internal static async Task DeleteAsync( ILogger logger, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; await cloudCodeService.DeleteAsync(projectId, environmentId, input.ScriptName, cancellationToken); diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/DeleteModuleHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/DeleteModuleHandler.cs index 77be32f..229fced 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/DeleteModuleHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/DeleteModuleHandler.cs @@ -30,7 +30,7 @@ internal static async Task DeleteModuleAsync( CancellationToken cancellationToken ) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; await cloudCodeService.DeleteModuleAsync(projectId, environmentId, input.ModuleName, cancellationToken); diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/GetHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/GetHandler.cs index a7b616a..ca0d088 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/GetHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/GetHandler.cs @@ -31,7 +31,7 @@ internal static async Task GetAsync( CancellationToken cancellationToken) { var scriptName = input.ScriptName; - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; var script = await cloudCodeService.GetAsync(projectId, environmentId, scriptName!, cancellationToken); diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/GetModuleHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/GetModuleHandler.cs index 27d12c4..0d0dc9b 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/GetModuleHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/GetModuleHandler.cs @@ -36,7 +36,7 @@ internal static async Task GetModuleAsync( ILogger logger, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; var moduleName = input.ModuleName; diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/ListHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/ListHandler.cs index 43d3cd8..03107a4 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/ListHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/ListHandler.cs @@ -30,13 +30,13 @@ internal static async Task ListAsync( ILogger logger, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; var scripts = await cloudCodeService.ListAsync( projectId, environmentId, cancellationToken); var result = scripts - .Select(s => new CloudListScriptResult(s.Name, s.LastPublishedDate) ) + .Select(s => new CloudListScriptResult(s.Name, s.LastPublishedDate)) .ToList(); logger.LogResultValue(result); diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/ListModulesHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/ListModulesHandler.cs index f4d9c65..30d050f 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/ListModulesHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/ListModulesHandler.cs @@ -33,7 +33,7 @@ internal static async Task ListModulesAsync( CancellationToken cancellationToken) { var projectId = input.CloudProjectId!; - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var modules = await cloudCodeService .ListModulesAsync(projectId, environmentId, cancellationToken); diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/PublishHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/PublishHandler.cs index 22bc487..cadbe2a 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/PublishHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/PublishHandler.cs @@ -30,7 +30,7 @@ internal static async Task PublishAsync( { var scriptName = input.ScriptName; var version = input.Version ?? 0; - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; var result = await cloudCodeService.PublishAsync( projectId, environmentId, scriptName!, version, cancellationToken); diff --git a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/UpdateHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/UpdateHandler.cs index 1a0fa5d..35dee34 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/UpdateHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.CloudCode/Handlers/UpdateHandler.cs @@ -34,7 +34,7 @@ internal static async Task UpdateAsync( StatusContext? loadingContext, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; loadingContext?.Status("Loading script..."); diff --git a/Unity.Services.Cli/Unity.Services.Cli.Common.UnitTest/Console/CliPromptTest.cs b/Unity.Services.Cli/Unity.Services.Cli.Common.UnitTest/Console/CliPromptTest.cs index f105a9e..abebccf 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Common.UnitTest/Console/CliPromptTest.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Common.UnitTest/Console/CliPromptTest.cs @@ -22,7 +22,7 @@ public void SetUp() public async Task PromptAsyncSucceed() { const string expectedString = "foo"; - var prompt = new CliPrompt(m_MockAnsiConsole.Object); + var prompt = new CliPrompt(m_MockAnsiConsole.Object, false); m_MockPrompt.Setup(p => p.ShowAsync(m_MockAnsiConsole.Object, CancellationToken.None)) .ReturnsAsync(expectedString); diff --git a/Unity.Services.Cli/Unity.Services.Cli.Common/CommonModule.cs b/Unity.Services.Cli/Unity.Services.Cli.Common/CommonModule.cs index 7873edf..4f836cf 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Common/CommonModule.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Common/CommonModule.cs @@ -141,7 +141,7 @@ internal static void CreateAndRegisterCliPromptService(IServiceCollection servic Interactive = InteractionSupport.Yes }; var console = AnsiConsole.Create(settings); - serviceCollection.AddSingleton(new CliPrompt(console)); + serviceCollection.AddSingleton(new CliPrompt(console, System.Console.IsInputRedirected)); } public static TelemetrySender CreateTelemetrySender(ISystemEnvironmentProvider systemEnvironmentProvider) diff --git a/Unity.Services.Cli/Unity.Services.Cli.Common/Configuration/Validator/ConfigurationValidator.cs b/Unity.Services.Cli/Unity.Services.Cli.Common/Configuration/Validator/ConfigurationValidator.cs index db9ccdd..9a1496f 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Common/Configuration/Validator/ConfigurationValidator.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Common/Configuration/Validator/ConfigurationValidator.cs @@ -74,7 +74,7 @@ public void ThrowExceptionIfConfigInvalid(string key, string value) static bool IsEnvironmentIdValid(string value, out string errorMessage) { - var guidRegex = new Regex(k_GuidRegexPattern); + var guidRegex = new Regex(k_GuidRegexPattern, RegexOptions.None, TimeSpan.FromSeconds(1)); if (value.Any(char.IsWhiteSpace) || !guidRegex.IsMatch(value)) { errorMessage = GuidInvalidMessage; @@ -87,7 +87,7 @@ static bool IsEnvironmentIdValid(string value, out string errorMessage) static bool IsEnvironmentNameValid(string value, out string errorMessage) { - var guidRegex = new Regex(k_EnvironmentNameRegexPattern); + var guidRegex = new Regex(k_EnvironmentNameRegexPattern, RegexOptions.None, TimeSpan.FromSeconds(1)); if (value.Any(char.IsWhiteSpace) || !guidRegex.IsMatch(value)) { errorMessage = EnvironmentNameInvalidMessage; @@ -100,7 +100,7 @@ static bool IsEnvironmentNameValid(string value, out string errorMessage) static bool IsProjectIdValid(string value, out string errorMessage) { - var guidRegex = new Regex(k_GuidRegexPattern); + var guidRegex = new Regex(k_GuidRegexPattern, RegexOptions.None, TimeSpan.FromSeconds(1)); if (value.Any(char.IsWhiteSpace) || !guidRegex.IsMatch(value)) { errorMessage = GuidInvalidMessage; diff --git a/Unity.Services.Cli/Unity.Services.Cli.Common/Console/CliPrompt.cs b/Unity.Services.Cli/Unity.Services.Cli.Common/Console/CliPrompt.cs index 9799496..f095fdd 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Common/Console/CliPrompt.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Common/Console/CliPrompt.cs @@ -5,10 +5,12 @@ namespace Unity.Services.Cli.Common.Console; class CliPrompt : ICliPrompt { readonly IAnsiConsole m_Console; + public bool IsStandardInputRedirected { get; } - public CliPrompt(IAnsiConsole console) + public CliPrompt(IAnsiConsole console, bool isStandardInputRedirected) { m_Console = console; + IsStandardInputRedirected = isStandardInputRedirected; } public Task PromptAsync(IPrompt prompt, CancellationToken cancellationToken) diff --git a/Unity.Services.Cli/Unity.Services.Cli.Common/Console/ICliPrompt.cs b/Unity.Services.Cli/Unity.Services.Cli.Common/Console/ICliPrompt.cs index 6fb47af..37fac72 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Common/Console/ICliPrompt.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Common/Console/ICliPrompt.cs @@ -7,6 +7,12 @@ namespace Unity.Services.Cli.Common.Console; /// public interface ICliPrompt { + /// + /// Is the standard input redirected. + /// Should default to System.Console.IsInputRedirected. + /// + bool IsStandardInputRedirected { get; } + /// /// Execute expected prompt and return user input for the prompt. /// diff --git a/Unity.Services.Cli/Unity.Services.Cli.Common/Telemetry/AnalyticEvent/AnalyticEventUtils.cs b/Unity.Services.Cli/Unity.Services.Cli.Common/Telemetry/AnalyticEvent/AnalyticEventUtils.cs index bb6b7fd..2f23db5 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Common/Telemetry/AnalyticEvent/AnalyticEventUtils.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Common/Telemetry/AnalyticEvent/AnalyticEventUtils.cs @@ -14,6 +14,12 @@ public static string ConvertSymbolResultToString(SymbolResult symbol) symbol = symbol.Parent!; } symbolNames.Reverse(); + + if (symbolNames.FirstOrDefault() != null) + { + symbolNames[0] = "ugs"; + } + return string.Join("_", symbolNames); } } diff --git a/Unity.Services.Cli/Unity.Services.Cli.Common/Unity.Services.Cli.Common.csproj b/Unity.Services.Cli/Unity.Services.Cli.Common/Unity.Services.Cli.Common.csproj index 4553baf..217fc50 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Common/Unity.Services.Cli.Common.csproj +++ b/Unity.Services.Cli/Unity.Services.Cli.Common/Unity.Services.Cli.Common.csproj @@ -22,7 +22,7 @@ - + diff --git a/Unity.Services.Cli/Unity.Services.Cli.Common/Utils/IUnityEnvironment.cs b/Unity.Services.Cli/Unity.Services.Cli.Common/Utils/IUnityEnvironment.cs index b9d7a01..fb1fbba 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Common/Utils/IUnityEnvironment.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Common/Utils/IUnityEnvironment.cs @@ -12,12 +12,15 @@ public interface IUnityEnvironment /// /// Gets the Unity environment id from the current saved environment name /// + /// /// environment id - public Task FetchIdentifierAsync(); + public Task FetchIdentifierAsync(CancellationToken cancellationToken); + /// /// Gets the Unity environment id from that matches the environment name passed as parameter /// /// + /// /// - public Task FetchIdentifierFromSpecificEnvironmentNameAsync(string environmentName); + public Task FetchIdentifierFromSpecificEnvironmentNameAsync(string environmentName, CancellationToken cancellationToken); } diff --git a/Unity.Services.Cli/Unity.Services.Cli.Environment.UnitTest/Handlers/DeletionHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Environment.UnitTest/Handlers/DeletionHandlerTests.cs index c6c4552..694bb75 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Environment.UnitTest/Handlers/DeletionHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Environment.UnitTest/Handlers/DeletionHandlerTests.cs @@ -71,7 +71,7 @@ public async Task DeleteAsync_ProjectIdOptionDoNotRunConfiguration() }; m_MockUnityEnvironment.Setup(c => - c.FetchIdentifierFromSpecificEnvironmentNameAsync(k_ValidEnvironmentName)) + c.FetchIdentifierFromSpecificEnvironmentNameAsync(k_ValidEnvironmentName, CancellationToken.None)) .ReturnsAsync(mockEnvironmentId); await DeletionHandler.DeleteAsync(input, m_MockHelper.MockEnvironment.Object, m_MockHelper.MockLogger.Object, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Environment.UnitTest/UnityEnvironmentTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Environment.UnitTest/UnityEnvironmentTests.cs index 43295f2..8f735ba 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Environment.UnitTest/UnityEnvironmentTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Environment.UnitTest/UnityEnvironmentTests.cs @@ -44,7 +44,7 @@ public void FetchIdentifierAsyncThrowsExceptionWhenMissingConfig(string? name, s m_UnityEnvironment!.SetName(name); m_UnityEnvironment!.SetProjectId(projectId); - Assert.ThrowsAsync(() => m_UnityEnvironment!.FetchIdentifierAsync()); + Assert.ThrowsAsync(() => m_UnityEnvironment!.FetchIdentifierAsync(CancellationToken.None)); } [Test] @@ -60,7 +60,7 @@ public void FetchIdentifierAsyncThrowsExceptionWhenEnvironmentNameNotFound() m_MockEnvService.Setup(a => a.ListAsync(It.IsAny(), It.IsAny())) .Returns(Task.FromResult(responses)); - Assert.ThrowsAsync(() => m_UnityEnvironment!.FetchIdentifierAsync()); + Assert.ThrowsAsync(() => m_UnityEnvironment!.FetchIdentifierAsync(CancellationToken.None)); } [Test] @@ -82,7 +82,7 @@ public async Task FetchIdentifierAsyncReturnsIdWhenMatchesEnvironmentName() a.ListAsync(k_ValidProjectId, It.IsAny())) .Returns(Task.FromResult(responses)); - var result = await m_UnityEnvironment!.FetchIdentifierAsync(); + var result = await m_UnityEnvironment!.FetchIdentifierAsync(CancellationToken.None); Assert.AreEqual(mockEnvironmentId, result!); } } diff --git a/Unity.Services.Cli/Unity.Services.Cli.Environment/Handlers/DeletionHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Environment/Handlers/DeletionHandler.cs index 5f8b39a..94c6f0f 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Environment/Handlers/DeletionHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Environment/Handlers/DeletionHandler.cs @@ -24,7 +24,7 @@ internal static async Task DeleteAsync(EnvironmentInput input, IEnvironmentServi var projectId = input.CloudProjectId; var environmentId = - await unityEnvironment.FetchIdentifierFromSpecificEnvironmentNameAsync(environmentName!); + await unityEnvironment.FetchIdentifierFromSpecificEnvironmentNameAsync(environmentName!, cancellationToken); await environmentService.DeleteAsync(projectId!, environmentId!, cancellationToken); logger.LogInformation("Deleted environment '{environmentName}'", environmentName); diff --git a/Unity.Services.Cli/Unity.Services.Cli.Environment/Service/UnityEnvironment.cs b/Unity.Services.Cli/Unity.Services.Cli.Environment/Service/UnityEnvironment.cs index 3307784..aa2bbe6 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Environment/Service/UnityEnvironment.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Environment/Service/UnityEnvironment.cs @@ -33,7 +33,7 @@ public UnityEnvironment(IEnvironmentService envService, IConfigurationValidator } /// - public async Task FetchIdentifierFromSpecificEnvironmentNameAsync(string environmentName) + public async Task FetchIdentifierFromSpecificEnvironmentNameAsync(string environmentName, CancellationToken cancellationToken) { if (string.IsNullOrEmpty(ProjectId)) { @@ -45,7 +45,7 @@ public async Task FetchIdentifierFromSpecificEnvironmentNameAsync(string m_ConfigValidator.ThrowExceptionIfConfigInvalid(Keys.ConfigKeys.EnvironmentName, environmentName); var environments = await m_EnvironmentService! - .ListAsync(ProjectId!, CancellationToken.None); + .ListAsync(ProjectId!, cancellationToken); var environmentId = environments.ToList().Find(a => a.Name == environmentName)?.Id; @@ -58,7 +58,7 @@ public async Task FetchIdentifierFromSpecificEnvironmentNameAsync(string } /// - public async Task FetchIdentifierAsync() + public async Task FetchIdentifierAsync(CancellationToken cancellationToken) { if (string.IsNullOrEmpty(Name)) { @@ -67,6 +67,6 @@ public async Task FetchIdentifierAsync() Keys.EnvironmentKeys.EnvironmentName); } - return await FetchIdentifierFromSpecificEnvironmentNameAsync(m_Name!); + return await FetchIdentifierFromSpecificEnvironmentNameAsync(m_Name!, cancellationToken); } } diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/GameServerHostingModuleTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/GameServerHostingModuleTests.cs new file mode 100644 index 0000000..7de75bd --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/GameServerHostingModuleTests.cs @@ -0,0 +1,213 @@ +using System.CommandLine.Builder; +using System.Net; +using System.Reflection; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Moq; +using Unity.Services.Cli.Common; +using Unity.Services.Cli.Common.Networking; +using Unity.Services.Cli.GameServerHosting.Endpoints; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Cli.GameServerHosting.Services; +using Unity.Services.Cli.ServiceAccountAuthentication; +using Unity.Services.Cli.TestUtils; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Client; +using Unity.Services.Multiplay.Authoring.Core; +using Unity.Services.Multiplay.Authoring.Core.Deployment; +using Unity.Services.Multiplay.Authoring.Core.MultiplayApi; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest; + +public class GameServerHostingModuleTests +{ + static readonly GameServerHostingModule k_GshModule = new(); + + [Test] + public void ValidateBuildCommands() + { + var commandLineBuilder = new CommandLineBuilder(); + commandLineBuilder.AddModule(k_GshModule); + + TestsHelper.AssertContainsCommand( + k_GshModule.ModuleRootCommand, + k_GshModule.BuildCommand.Name, + out var resultCommand + ); + + Assert.Multiple( + () => + { + Assert.That(resultCommand, Is.EqualTo(k_GshModule.BuildCommand)); + Assert.That(k_GshModule.BuildCreateCommand.Handler, Is.Not.Null); + Assert.That(k_GshModule.BuildDeleteCommand.Handler, Is.Not.Null); + Assert.That(k_GshModule.BuildGetCommand.Handler, Is.Not.Null); + Assert.That(k_GshModule.BuildInstallsCommand.Handler, Is.Not.Null); + Assert.That(k_GshModule.BuildListCommand.Handler, Is.Not.Null); + Assert.That(k_GshModule.BuildUpdateCommand.Handler, Is.Not.Null); + } + ); + } + + [Test] + public void ValidateFleetCommands() + { + var commandLineBuilder = new CommandLineBuilder(); + commandLineBuilder.AddModule(k_GshModule); + + TestsHelper.AssertContainsCommand( + k_GshModule.ModuleRootCommand, + k_GshModule.FleetCommand.Name, + out var resultCommand + ); + + Assert.Multiple( + () => + { + Assert.That(resultCommand, Is.EqualTo(k_GshModule.FleetCommand)); + Assert.That(k_GshModule.FleetCreateCommand.Handler, Is.Not.Null); + Assert.That(k_GshModule.FleetDeleteCommand.Handler, Is.Not.Null); + Assert.That(k_GshModule.FleetGetCommand.Handler, Is.Not.Null); + Assert.That(k_GshModule.FleetListCommand.Handler, Is.Not.Null); + Assert.That(k_GshModule.FleetUpdateCommand.Handler, Is.Not.Null); + } + ); + } + + [Test] + public void ValidatFleetRegionCommands() + { + var commandLineBuilder = new CommandLineBuilder(); + commandLineBuilder.AddModule(k_GshModule); + TestsHelper.AssertContainsCommand( + k_GshModule.ModuleRootCommand, + k_GshModule.FleetRegionCommand.Name, + out var resultCommand + ); + + Assert.Multiple( + () => + { + Assert.That(resultCommand, Is.EqualTo(k_GshModule.FleetRegionCommand)); + Assert.That(k_GshModule.FleetRegionTemplatesCommand.Handler, Is.Not.Null); + Assert.That(k_GshModule.FleetRegionAvailableCommand.Handler, Is.Not.Null); + Assert.That(k_GshModule.FleetRegionCreateCommand.Handler, Is.Not.Null); + } + ); + } + + [Test] + public void ValidateServerCommands() + { + var commandLineBuilder = new CommandLineBuilder(); + commandLineBuilder.AddModule(k_GshModule); + TestsHelper.AssertContainsCommand( + k_GshModule.ModuleRootCommand, + k_GshModule.ServerCommand.Name, + out var resultCommand + ); + + Assert.Multiple( + () => + { + Assert.That(resultCommand, Is.EqualTo(k_GshModule.ServerCommand)); + Assert.That(k_GshModule.ServerGetCommand.Handler, Is.Not.Null); + Assert.That(k_GshModule.ServerListCommand.Handler, Is.Not.Null); + } + ); + } + + [Test] + public void ExceptionFactory_NullException() + { + var response = new ApiResponse(HttpStatusCode.OK, "data", "raw content"); + + var exception = GameServerHostingModule.ExceptionFactory("test", response); + + Assert.That(exception, Is.Null); + } + + [TestCase(HttpStatusCode.BadRequest, "Bad Request")] + [TestCase(HttpStatusCode.OK, "Failed Deserialization")] + public void ExceptionFactory_CreateException(HttpStatusCode statusCode, string errorText) + { + const string methodName = "test"; + const string rawContent = "raw content"; + const string data = "data"; + + var response = new ApiResponse(statusCode, data, rawContent) + { + ErrorText = errorText + }; + + var exception = GameServerHostingModule.ExceptionFactory(methodName, response); + + var expectedMessage = statusCode >= HttpStatusCode.BadRequest + ? $"Error calling {methodName}: {rawContent}" + : $"Error calling {methodName}: {errorText}"; + + var expectedContent = statusCode >= HttpStatusCode.BadRequest + ? rawContent + : data; + + Assert.That(exception, Is.Not.Null); + Assert.That(exception, Is.TypeOf(typeof(ApiException))); + var apiException = (ApiException)exception; + Assert.Multiple( + () => + { + Assert.That(apiException.ErrorCode, Is.EqualTo((int)statusCode)); + Assert.That(apiException.Message, Is.EqualTo(expectedMessage)); + Assert.That(apiException.ErrorContent, Is.EqualTo(expectedContent)); + }); + } + + [TestCase(typeof(IBuildsApiAsync))] + [TestCase(typeof(IBuildConfigurationsApiAsync))] + [TestCase(typeof(IFleetsApiAsync))] + [TestCase(typeof(GameServerHostingApiConfig))] + [TestCase(typeof(IBuildsApiFactory))] + [TestCase(typeof(IBuildConfigApiFactory))] + [TestCase(typeof(IFleetApiFactory))] + [TestCase(typeof(IGameServerHostingService))] + [TestCase(typeof(MultiplayDeployer))] + [TestCase(typeof(IDeploymentFacadeFactory))] + [TestCase(typeof(IDeploymentFacade))] + [TestCase(typeof(IMultiplayBuildAuthoring))] + [TestCase(typeof(IBinaryBuilder))] + [TestCase(typeof(IBuildFileManagement))] + public void GameServerHostingModule_RegistersServices(Type serviceType) + { + var types = new List + { + typeof(UnityServicesGatewayEndpoints).GetTypeInfo(), + typeof(CloudContentDeliveryEndpoints).GetTypeInfo() + }; + EndpointHelper.InitializeNetworkTargetEndpoints(types); + var collection = new ServiceCollection(); + collection.AddSingleton(new Mock().Object); + GameServerHostingModule.RegisterServices(new HostBuilderContext(new Dictionary()), collection); + + Assert.That(collection.FirstOrDefault(c => c.ServiceType == serviceType), Is.Not.Null); + } + + [Test] + public void ConfigureGameServerHostingRegistersExpectedServices() + { + var services = new List + { + ServiceDescriptor.Singleton(new Mock().Object) + }; + + var hostBuilder = TestsHelper.CreateAndSetupMockHostBuilder(services); + EndpointHelper.InitializeNetworkTargetEndpoints( + new[] + { + typeof(UnityServicesGatewayEndpoints).GetTypeInfo(), + typeof(CloudContentDeliveryEndpoints).GetTypeInfo() + }); + hostBuilder.ConfigureServices(GameServerHostingModule.RegisterServices); + + TestsHelper.AssertHasServiceSingleton(services); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/GameServerHostingUnitTestsConstants.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/GameServerHostingUnitTestsConstants.cs new file mode 100644 index 0000000..789ae5d --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/GameServerHostingUnitTestsConstants.cs @@ -0,0 +1,74 @@ +namespace Unity.Services.Cli.GameServerHosting.UnitTest; + +public static class GameServerHostingUnitTestsConstants +{ + public const string TestAccessToken = "testAccessToken"; + public const string ValidProjectId = "00000000-0000-0000-0000-000000000010"; + public const string InvalidProjectId = "00000000-0000-0000-0000-000000000011"; + public const string ValidEnvironmentName = "production"; + public const string ValidEnvironmentId = "00000000-0000-0000-0000-000000000000"; + public const string InvalidEnvironmentId = "00000000-0000-0000-0000-000000000001"; + public const string ValidBuildName = "Build One"; + public const string ValidBuildId = "11"; + public const string InvalidBuildId = "1234"; + public const string ValidBucketId = "11111000-0000-0000-0000-000000000000"; + public const string ValidReleaseId = "00000000-1100-0000-0000-000000000000"; + public const string ValidContainerTag = "v1"; + + // Build File constants + public const long BuildWithOneFileId = 121; + public const long BuildWithTwoFilesId = 122; + public const long ValidBuildIdBucket = 201; + public const long ValidBuildIdContainer = 202; + public const long ValidBuildIdFileUpload = 203; + public const long SyncingBuildId = 333; + + // Filenames + public const string BuildWithOneFileFileName = "game_binary.txt"; + public const string BuildWithOneToBeDeletedFileFileName = "file_to_be_deleted.txt"; + + // Fleet specific constants + public const string ValidFleetId = "00000000-0000-0000-1000-000000000000"; + public const string ValidFleetId2 = "00000000-0000-0000-1100-000000000000"; + + public const string InvalidFleetId = "00000000-0000-0000-2222-000000000000"; + + public const string ValidFleetName = "Fleet One"; + public const string ValidFleetName2 = "Fleet Two"; + + public const string OsNameLinux = "Linux"; + + // Build Configuration specific constants + public const long ValidBuildConfigurationId = 1L; + public const long ValidBuildConfigurationBuildId = 1L; + public const long InvalidBuildConfigurationId = 666L; + public const string ValidBuildConfigurationName = "Build Configuration One"; + public const string ValidBuildConfigurationBinaryPath = "/test.exe"; + public const string ValidBuildConfigurationCommandLine = "--init test"; + public const string ValidBuildConfigurationConfiguration = "hello:world"; + + public const string ValidBuildConfigurationQueryType = "a2s"; + + + public const string ValidRegionId = "00000000-0000-0000-0000-000000000000"; + public const string ValidRegionId2 = "00000000-0000-0000-0000-000000000001"; + public const string InvalidRegionId = "00000000-0000-0000-0000-000000000002"; + public const string InvalidUuid = "00000000-0000-0000-ZZZZ-000000000000"; + + public const string ValidTemplateRegionName = "us-west1"; + public const string ValidTemplateRegionId = "00000000-0000-0000-aaaa-110000000000"; + + public const string ValidTemplateRegionName2 = "europe-west2"; + public const string ValidTemplateRegionId2 = "00000000-0000-0000-aaaa-220000000000"; + + public const string ValidFleetRegionId = "00000000-0000-0000-aaaa-300000000000"; + + public const long ValidServerId = 123456L; + public const long InvalidServerId = 666L; + + public const long ValidMachineId = 654321L; + public const long InvalidMachineId = 666L; + + public const long ValidLocationId = 111111L; + public const string ValidLocationName = "us-west1"; +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationCreateHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationCreateHandlerTests.cs new file mode 100644 index 0000000..578bb97 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationCreateHandlerTests.cs @@ -0,0 +1,251 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class BuildConfigurationCreateHandlerTests : HandlerCommon +{ + [Test] + public async Task BuildConfigurationCreateAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await BuildConfigurationCreateHandler.BuildConfigurationCreateAsync( + null!, + MockUnityEnvironment.Object, + null!, + null!, + mockLoadingIndicator.Object, + CancellationToken.None); + + mockLoadingIndicator.Verify( + ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), + Times.Once); + } + + [Test] + public async Task BuildConfigurationCreateAsync_CallsFetchIdentifierAsync() + { + BuildConfigurationCreateInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BinaryPath = "/test.exe", + BuildId = 123, + Configuration = new List(), + CommandLine = "--init test", + Cores = 1, + Memory = 100, + Name = "test-build-config", + QueryType = "none", + Speed = 100 + }; + + await BuildConfigurationCreateHandler.BuildConfigurationCreateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase( + null, + ValidBuildConfigurationId, + ValidBuildConfigurationCommandLine, + ValidBuildConfigurationConfiguration, + 100, + 100, + ValidBuildConfigurationName, + ValidBuildConfigurationQueryType, + 100, + TestName = "Missing BinaryPath")] + [TestCase( + ValidBuildConfigurationBinaryPath, + null, + ValidBuildConfigurationCommandLine, + ValidBuildConfigurationConfiguration, + 100, + 100, + ValidBuildConfigurationName, + ValidBuildConfigurationQueryType, + 100, + TestName = "Missing BuildId")] + [TestCase( + ValidBuildConfigurationBinaryPath, + ValidBuildConfigurationId, + null, + ValidBuildConfigurationConfiguration, + 100, + 100, + ValidBuildConfigurationName, + ValidBuildConfigurationQueryType, + 100, + TestName = "Missing CommandLine")] + [TestCase( + ValidBuildConfigurationBinaryPath, + ValidBuildConfigurationId, + ValidBuildConfigurationCommandLine, + null, + 100, + 100, + ValidBuildConfigurationName, + ValidBuildConfigurationQueryType, + 100, + TestName = "Missing Configuration")] + [TestCase( + ValidBuildConfigurationBinaryPath, + ValidBuildConfigurationId, + ValidBuildConfigurationCommandLine, + ValidBuildConfigurationConfiguration, + null, + 100, + ValidBuildConfigurationName, + ValidBuildConfigurationQueryType, + 100, + TestName = "Missing Cores")] + [TestCase( + ValidBuildConfigurationBinaryPath, + ValidBuildConfigurationId, + ValidBuildConfigurationCommandLine, + ValidBuildConfigurationConfiguration, + 100, + null, + ValidBuildConfigurationName, + ValidBuildConfigurationQueryType, + 100, + TestName = "Missing Memory")] + [TestCase( + ValidBuildConfigurationBinaryPath, + ValidBuildConfigurationId, + ValidBuildConfigurationCommandLine, + ValidBuildConfigurationConfiguration, + 100, + 100, + null, + ValidBuildConfigurationQueryType, + 100, + TestName = "Missing Name")] + [TestCase( + ValidBuildConfigurationBinaryPath, + ValidBuildConfigurationId, + ValidBuildConfigurationCommandLine, + ValidBuildConfigurationConfiguration, + 100, + 100, + ValidBuildConfigurationName, + null, + 100, + TestName = "Missing QueryType")] + [TestCase( + ValidBuildConfigurationBinaryPath, + ValidBuildConfigurationId, + ValidBuildConfigurationCommandLine, + ValidBuildConfigurationConfiguration, + 100, + 100, + ValidBuildConfigurationName, + ValidBuildConfigurationQueryType, + null, + TestName = "Missing Speed")] + public Task BuildConfigurationCreateAsync_NullInputThrowsException( + string? binaryPath, + long? buildId, + string? commandLine, + string? configuration, + long? cores, + long? memory, + string? name, + string? queryType, + long? speed) + { + BuildConfigurationCreateInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BinaryPath = binaryPath, + BuildId = buildId, + CommandLine = commandLine, + Configuration = configuration == null + ? null + : new List + { + configuration + }, + Cores = cores, + Memory = memory, + Name = name, + QueryType = queryType, + Speed = speed + }; + + Assert.ThrowsAsync( + () => + BuildConfigurationCreateHandler.BuildConfigurationCreateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled( + MockLogger!, + LogLevel.Critical, + LoggerExtension.ResultEventId, + Times.Never); + return Task.CompletedTask; + } + + [TestCase("invalid", TestName = "No Separator")] + [TestCase("key:value:value", TestName = "Too Many Separators")] + [TestCase("", TestName = "Empty")] + public Task BuildConfigurationCreateAsync_InvalidConfigurationInputThrowsException(string? configuration) + { + BuildConfigurationCreateInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BinaryPath = ValidBuildConfigurationBinaryPath, + BuildId = ValidBuildConfigurationBuildId, + CommandLine = ValidBuildConfigurationCommandLine, + Configuration = new List { configuration! }, + Cores = 100, + Memory = 100, + Name = ValidBuildConfigurationName, + QueryType = ValidBuildConfigurationQueryType, + Speed = 100 + }; + + Assert.ThrowsAsync( + () => + BuildConfigurationCreateHandler.BuildConfigurationCreateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled( + MockLogger!, + LogLevel.Critical, + LoggerExtension.ResultEventId, + Times.Never); + return Task.CompletedTask; + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationDeleteHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationDeleteHandlerTests.cs new file mode 100644 index 0000000..e2cb761 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationDeleteHandlerTests.cs @@ -0,0 +1,119 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class BuildConfigurationDeleteHandlerTests : HandlerCommon +{ + [Test] + public async Task BuildConfigurationDeleteAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await BuildConfigurationDeleteHandler.BuildConfigurationDeleteAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task BuildConfigurationDeleteAsync_CallsFetchIdentifierAsync() + { + BuildConfigurationIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildConfigurationId = ValidBuildConfigurationId.ToString() + }; + + await BuildConfigurationDeleteHandler.BuildConfigurationDeleteAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [Test] + public void BuildConfigurationDeleteAsync_NullBuildIdThrowsException() + { + BuildConfigurationIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildConfigurationId = null + }; + + Assert.ThrowsAsync(() => + BuildConfigurationDeleteHandler.BuildConfigurationDeleteAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + BuildConfigurationsApi!.DefaultBuildConfigurationsClient.Verify(api => api.DeleteBuildConfigurationAsync( + It.IsAny(), It.IsAny(), + It.IsAny(), null, 0, CancellationToken.None + ), Times.Never); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } + + [Test] + public async Task BuildConfigurationDeleteAsync_CallsDeleteService() + { + BuildConfigurationIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildConfigurationId = ValidBuildConfigurationId.ToString() + }; + await BuildConfigurationDeleteHandler.BuildConfigurationDeleteAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + BuildConfigurationsApi!.DefaultBuildConfigurationsClient.Verify(api => api.DeleteBuildConfigurationAsync( + new Guid(ValidProjectId), new Guid(ValidEnvironmentId), + ValidBuildConfigurationId, null, 0, CancellationToken.None + ), Times.Once); + } + + [Test] + public void BuildConfigurationDeleteAsync_InvalidInputThrowsException() + { + BuildConfigurationIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildConfigurationId = "invalid" + }; + Assert.ThrowsAsync(() => + BuildConfigurationDeleteHandler.BuildConfigurationDeleteAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationGetHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationGetHandlerTests.cs new file mode 100644 index 0000000..4a55c51 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationGetHandlerTests.cs @@ -0,0 +1,172 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.TestUtils; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class BuildConfigurationGetHandlerTests : HandlerCommon +{ + [Test] + public async Task BuildConfigurationGetAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await BuildConfigurationGetHandler.BuildConfigurationGetAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task BuildConfigurationGetAsync_CallsFetchIdentifierAsync() + { + BuildConfigurationIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildConfigurationId = ValidBuildConfigurationId.ToString() + }; + + await BuildConfigurationGetHandler.BuildConfigurationGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [Test] + public void BuildConfigurationGetAsync_NullBuildIdThrowsException() + { + BuildConfigurationIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildConfigurationId = null + }; + + Assert.ThrowsAsync(() => + BuildConfigurationGetHandler.BuildConfigurationGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + BuildConfigurationsApi!.DefaultBuildConfigurationsClient.Verify(api => api.GetBuildConfigurationAsync( + It.IsAny(), It.IsAny(), + It.IsAny(), 0, CancellationToken.None + ), Times.Never); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } + + [Test] + public async Task BuildConfigurationGetAsync_CallsGetService() + { + BuildConfigurationIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildConfigurationId = ValidBuildConfigurationId.ToString() + }; + await BuildConfigurationGetHandler.BuildConfigurationGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + BuildConfigurationsApi!.DefaultBuildConfigurationsClient.Verify(api => api.GetBuildConfigurationAsync( + new Guid(ValidProjectId), + new Guid(ValidEnvironmentId), + ValidBuildConfigurationId, + 0, + CancellationToken.None + ), Times.Once); + } + + [Test] + public void BuildConfigurationGetAsync_InvalidInputThrowsException() + { + BuildConfigurationIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildConfigurationId = "invalid" + }; + Assert.ThrowsAsync(() => + BuildConfigurationGetHandler.BuildConfigurationGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } + + [Test] + public async Task BuildConfigurationGetAsync_ValidateLoggingOutput() + { + + var m_BuildConfiguration = new BuildConfiguration( + binaryPath: "/path/to/simple-go-server", + buildID: long.Parse(ValidBuildId), + buildName: ValidBuildName, + commandLine: "simple-go-server", + configuration: new List() + { + new ConfigEntry( + id: 0, + key: "key", + value: "value" + ), + }, + cores: 2L, + createdAt: new DateTime(2022, 10, 11), + fleetID: new Guid(ValidFleetId), + fleetName: ValidFleetName, + id: ValidBuildConfigurationId, + memory: 800L, + name: ValidBuildConfigurationName, + queryType: "sqp", + speed: 1200L, + updatedAt: new DateTime(2022, 10, 11), + version: 1L + ); + + BuildConfigurationIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildConfigurationId = ValidBuildConfigurationId.ToString() + }; + + await BuildConfigurationGetHandler.BuildConfigurationGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Once, new BuildConfigurationOutput(m_BuildConfiguration).ToString()); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationListHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationListHandlerTests.cs new file mode 100644 index 0000000..37ca5ea --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationListHandlerTests.cs @@ -0,0 +1,75 @@ +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class BuildConfigurationListHandlerTests : HandlerCommon +{ + [Test] + public async Task BuildConfigurationListAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await BuildConfigurationListHandler.BuildConfigurationListAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task BuildConfigurationListAsync_CallsFetchIdentifierAsync() + { + BuildConfigurationListInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName + }; + + await BuildConfigurationListHandler.BuildConfigurationListAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(null, null)] + [TestCase(ValidFleetId, null)] + [TestCase(null, ValidFleetName)] + public async Task BuildConfigurationListAsync_CallsListService( + string? fleetId, + string? partial + ) + { + Guid? fleetGuid = fleetId == null ? null : new Guid(fleetId); + + BuildConfigurationListInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + FleetId = fleetId, + Partial = partial + }; + + await BuildConfigurationListHandler.BuildConfigurationListAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + BuildConfigurationsApi!.DefaultBuildConfigurationsClient.Verify(api => api.ListBuildConfigurationsAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + fleetGuid, partial, 0, CancellationToken.None + ), Times.Once); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationUpdateHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationUpdateHandlerTests.cs new file mode 100644 index 0000000..f9296c0 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildConfigurationUpdateHandlerTests.cs @@ -0,0 +1,87 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class BuildConfigurationUpdateHandlerTests : HandlerCommon +{ + [Test] + public async Task BuildConfigurationUpdateAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await BuildConfigurationUpdateHandler.BuildConfigurationUpdateAsync( + null!, + MockUnityEnvironment.Object, + null!, + null!, + mockLoadingIndicator.Object, + CancellationToken.None); + + mockLoadingIndicator.Verify( + ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), + Times.Once); + } + + [Test] + public async Task BuildConfigurationUpdateAsync_CallsFetchIdentifierAsync() + { + BuildConfigurationUpdateInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildConfigId = ValidBuildConfigurationId, + }; + + await BuildConfigurationUpdateHandler.BuildConfigurationUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase("invalid", TestName = "No Separator")] + [TestCase("key:value:value", TestName = "Too Many Separators")] + [TestCase("", TestName = "Empty")] + public Task BuildConfigurationUpdateAsync_InvalidConfigurationInputThrowsException(string? configuration) + { + BuildConfigurationUpdateInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = ValidBuildConfigurationBuildId, + Configuration = new List { configuration! }, + }; + + Assert.ThrowsAsync( + () => + BuildConfigurationUpdateHandler.BuildConfigurationUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled( + MockLogger!, + LogLevel.Critical, + LoggerExtension.ResultEventId, + Times.Never); + return Task.CompletedTask; + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateHandlerTests.cs new file mode 100644 index 0000000..2823bf0 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateHandlerTests.cs @@ -0,0 +1,201 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Client; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using static Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model.CreateBuildRequest; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class BuildCreateHandlerTests : HandlerCommon +{ + [Test] + public async Task BuildCreateAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await BuildCreateHandler.BuildCreateAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task BuildCreateAsync_CallsFetchIdentifierAsync() + { + BuildCreateInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildName = "Build One", + BuildOsFamily = OsFamilyEnum.LINUX, + BuildType = BuildTypeEnum.FILEUPLOAD + }; + + await BuildCreateHandler.BuildCreateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, null, OsFamilyEnum.LINUX, BuildTypeEnum.FILEUPLOAD)] + [TestCase(ValidProjectId, ValidEnvironmentName, null, OsFamilyEnum.LINUX, BuildTypeEnum.CONTAINER)] + [TestCase(ValidProjectId, ValidEnvironmentName, null, OsFamilyEnum.LINUX, BuildTypeEnum.S3)] + public void BuildCreateAsync_NullBuildNameThrowsException( + string projectId, + string environmentName, + string buildName, + OsFamilyEnum osFamily, + BuildTypeEnum buildType + ) + { + BuildCreateInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + BuildName = buildName, + BuildOsFamily = osFamily, + BuildType = buildType + }; + + Assert.ThrowsAsync(() => + BuildCreateHandler.BuildCreateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + BuildsApi!.DefaultBuildsClient.Verify(api => api.CreateBuildAsync( + It.IsAny(), It.IsAny(), + It.IsAny(), 0, CancellationToken.None + ), Times.Never); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.FILEUPLOAD)] + [TestCase(ValidProjectId, ValidEnvironmentName, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.CONTAINER)] + [TestCase(ValidProjectId, ValidEnvironmentName, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.S3)] + public async Task BuildCreateAsync_CallsGetService( + string projectId, + string environmentName, + string buildName, + OsFamilyEnum osFamily, + BuildTypeEnum buildType + ) + { + BuildCreateInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + BuildName = buildName, + BuildOsFamily = osFamily, + BuildType = buildType + }; + + await BuildCreateHandler.BuildCreateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + BuildsApi!.DefaultBuildsClient.Verify(api => api.CreateBuildAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + new CreateBuildRequest(buildName, buildType, null!, osFamily), 0, CancellationToken.None + ), Times.Once); + } + + [TestCase(InvalidProjectId, InvalidEnvironmentId, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.FILEUPLOAD)] + [TestCase(ValidProjectId, InvalidEnvironmentId, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.FILEUPLOAD)] + [TestCase(InvalidProjectId, ValidEnvironmentId, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.FILEUPLOAD)] + [TestCase(InvalidProjectId, InvalidEnvironmentId, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.FILEUPLOAD)] + [TestCase(InvalidProjectId, InvalidEnvironmentId, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.CONTAINER)] + [TestCase(ValidProjectId, InvalidEnvironmentId, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.CONTAINER)] + [TestCase(InvalidProjectId, ValidEnvironmentId, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.CONTAINER)] + [TestCase(InvalidProjectId, InvalidEnvironmentId, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.CONTAINER)] + [TestCase(InvalidProjectId, InvalidEnvironmentId, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.S3)] + [TestCase(ValidProjectId, InvalidEnvironmentId, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.S3)] + [TestCase(InvalidProjectId, ValidEnvironmentId, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.S3)] + [TestCase(InvalidProjectId, InvalidEnvironmentId, ValidBuildName, OsFamilyEnum.LINUX, BuildTypeEnum.S3)] + public void BuildCreateAsync_InvalidInputThrowsException( + string projectId, + string environmentId, + string buildName, + OsFamilyEnum osFamily, + BuildTypeEnum buildType + ) + { + BuildCreateInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildName = buildName, + BuildOsFamily = osFamily, + BuildType = buildType + }; + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(environmentId); + + Assert.ThrowsAsync(() => + BuildCreateHandler.BuildCreateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } + + [TestCase(ValidProjectId, ValidEnvironmentId, "Build1", OsFamilyEnum.LINUX, BuildTypeEnum.FILEUPLOAD)] + [TestCase(ValidProjectId, ValidEnvironmentId, "Build1", OsFamilyEnum.LINUX, BuildTypeEnum.CONTAINER)] + public void BuildCreateAsync_DuplicateNameThrowsException( + string projectId, + string environmentId, + string buildName, + OsFamilyEnum osFamily, + BuildTypeEnum buildType + ) + { + BuildCreateInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildName = buildName, + BuildOsFamily = osFamily, + BuildType = buildType + }; + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(environmentId); + + Assert.ThrowsAsync(() => + BuildCreateHandler.BuildCreateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionBucketHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionBucketHandlerTests.cs new file mode 100644 index 0000000..e6eab30 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionBucketHandlerTests.cs @@ -0,0 +1,164 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +partial class BuildCreateVersionHandlerTests +{ + [TestCase(null, "secretKey", "bucketUrl")] + [TestCase("accessKey", null, "bucketUrl")] + [TestCase("accessKey", "secretKey", null)] + public void BuildCreateVersionAsync_Bucket_MissingInputThrowsException( + string? accessKey, + string? secretKey, + string? bucketUrl + ) + { + BuildCreateVersionInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = ValidBuildIdBucket.ToString(), + AccessKey = accessKey, + SecretKey = secretKey, + BucketUrl = bucketUrl + }; + + Assert.ThrowsAsync( + () => BuildCreateVersionHandler.BuildCreateVersionAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + new Mock().Object, + CancellationToken.None + ) + ); + + BuildsApi!.DefaultBuildsClient.Verify( + api => api.CreateNewBuildVersionAsync( + It.IsAny(), + It.IsAny(), + ValidBuildIdBucket, + It.IsAny(), + 0, + CancellationToken.None + ), + Times.Never); + + TestsHelper.VerifyLoggerWasCalled( + MockLogger!, + LogLevel.Critical, + LoggerExtension.ResultEventId, + Times.Never); + } + + [Test] + public async Task BuildCreateAsync_Bucket_CallsGetService() + { + BuildCreateVersionInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = ValidBuildIdBucket.ToString(), + AccessKey = "accessKey", + BucketUrl = "bucketUrl", + SecretKey = "secretKey" + }; + + await BuildCreateVersionHandler.BuildCreateVersionAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + MockHttpClient!.Object, + CancellationToken.None + ); + + BuildsApi!.DefaultBuildsClient.Verify( + api => api.CreateNewBuildVersionAsync( + new Guid(input.CloudProjectId), + new Guid(ValidEnvironmentId), + ValidBuildIdBucket, + It.IsAny(), + 0, + CancellationToken.None + ), + Times.Once); + } + + [TestCase( + null, + "s3://bucket.url", + "secretKey", + null, + null, + typeof(MissingInputException), + TestName = "Missing access key" + )] + [TestCase( + "accessKey", + null, + "secretKey", + null, + null, + typeof(MissingInputException), + TestName = "Missing bucket url" + )] + [TestCase( + "accessKey", + "s3://bucket.url", + null, + null, + null, + typeof(MissingInputException), + TestName = "Missing secret key" + )] + [TestCase( + "accessKey", + "s3://bucket.url", + "secretKey", + "v1", + null, + typeof(CliException), + TestName = "Container tag with S3 build" + )] + [TestCase( + "accessKey", + "s3://bucket.url", + "secretKey", + null, + "/path/to/files", + typeof(CliException), + TestName = "File directory with S3 build" + )] + public void ValidateInput_Bucket( + string? accessKey, + string? bucketUrl, + string? secretKey, + string? containerTag, + string? fileDirectory, + Type expectedExceptionType + ) + { + var input = new BuildCreateVersionInput + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + AccessKey = accessKey, + BucketUrl = bucketUrl, + SecretKey = secretKey, + ContainerTag = containerTag, + FileDirectory = fileDirectory, + }; + + Assert.Throws(expectedExceptionType, () => BuildCreateVersionHandler.ValidateBucketInput(input)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionContainerHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionContainerHandlerTests.cs new file mode 100644 index 0000000..ed5fb9e --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionContainerHandlerTests.cs @@ -0,0 +1,136 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +partial class BuildCreateVersionHandlerTests +{ + [Test] + public void BuildCreateVersionAsync_Container_MissingInputThrowsException() + { + BuildCreateVersionInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = ValidBuildIdContainer.ToString(), + ContainerTag = null + }; + + Assert.ThrowsAsync( + () => BuildCreateVersionHandler.BuildCreateVersionAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + new Mock().Object, + CancellationToken.None + ) + ); + + BuildsApi!.DefaultBuildsClient.Verify( + api => api.CreateNewBuildVersionAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + 0, + CancellationToken.None + ), + Times.Never); + + TestsHelper.VerifyLoggerWasCalled( + MockLogger!, + LogLevel.Critical, + LoggerExtension.ResultEventId, + Times.Never); + } + + [Test] + public async Task BuildCreateAsync_Container_CallsGetService() + { + BuildCreateVersionInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = ValidBuildIdContainer.ToString(), + ContainerTag = ValidContainerTag + }; + + await BuildCreateVersionHandler.BuildCreateVersionAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + MockHttpClient!.Object, + CancellationToken.None + ); + + BuildsApi!.DefaultBuildsClient.Verify( + api => api.CreateNewBuildVersionAsync( + new Guid(input.CloudProjectId), + new Guid(ValidEnvironmentId), + ValidBuildIdContainer, + It.IsAny(), + 0, + CancellationToken.None + ), + Times.Once); + } + + [TestCase( + null, + null, + null, + null, + null, + typeof(MissingInputException), + TestName = "Missing container tag" + )] + [TestCase( + null, + "s3://bucket.url", + null, + "v1", + null, + typeof(CliException), + TestName = "S3 bucket with container build" + )] + [TestCase( + null, + null, + null, + "v1", + "/path/to/files", + typeof(CliException), + TestName = "File directory with container build" + )] + public void ValidateInput_Container( + string? accessKey, + string? bucketUrl, + string? secretKey, + string? containerTag, + string? fileDirectory, + Type expectedExceptionType + ) + { + var input = new BuildCreateVersionInput + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + AccessKey = accessKey, + BucketUrl = bucketUrl, + SecretKey = secretKey, + ContainerTag = containerTag, + FileDirectory = fileDirectory, + }; + + Assert.Throws(expectedExceptionType, () => BuildCreateVersionHandler.ValidateContainerInput(input)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionFileUploadHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionFileUploadHandlerTests.cs new file mode 100644 index 0000000..0dd7515 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionFileUploadHandlerTests.cs @@ -0,0 +1,284 @@ +using System.Net; +using Microsoft.Extensions.Logging; +using Moq; +using Moq.Protected; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +partial class BuildCreateVersionHandlerTests +{ + string? m_TempDirectory; + string? m_TempDirectoryEmpty; + string? m_TempFilePath; + + void SetUpTempFiles() + { + m_TempDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(m_TempDirectory); + + if (BuildsApi == null) throw new Exception("static BuildsAPI not initialised"); + + // write a file to the temp directory + m_TempFilePath = Path.Combine(m_TempDirectory, BuildWithOneFileFileName); + File.WriteAllText(m_TempFilePath, "file content to upload"); + + // emptyDirectory + m_TempDirectoryEmpty = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + } + + [TestCase(null, "/path/to/files", false)] + [TestCase(ValidBuildIdContainer, null, false)] + [TestCase(ValidBuildIdContainer, "/path/to/files", null)] + public void BuildCreateVersionAsync_FileUpload_MissingInputThrowsException( + long? buildId, + string? directory, + bool? removeOldFiles + ) + { + BuildCreateVersionInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = buildId.HasValue ? buildId.ToString() : null, + FileDirectory = directory, + RemoveOldFiles = removeOldFiles + }; + + Assert.ThrowsAsync( + () => BuildCreateVersionHandler.BuildCreateVersionAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + new Mock().Object, + CancellationToken.None + ) + ); + + BuildsApi!.DefaultBuildsClient.Verify( + api => api.CreateNewBuildVersionAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + 0, + CancellationToken.None + ), + Times.Never); + + TestsHelper.VerifyLoggerWasCalled( + MockLogger!, + LogLevel.Critical, + LoggerExtension.ResultEventId, + Times.Never); + } + + [TestCase( + BuildWithOneFileId, + 1, + false, + 100)] + [TestCase( + BuildWithTwoFilesId, + 1, + true, + 100)] + [TestCase( + BuildWithTwoFilesId, + 1, + false, + 1)] + [TestCase( + BuildWithOneFileId, + 1, + false, + 2)] + public async Task BuildCreateVersionAsync_FileUpload_HandlesAFile( + long buildId, + int expectedSignedUrUploadsRequests, + bool removeOldFiles, + int limit + ) + { + var mockMessageHandler = new Mock(MockBehavior.Strict); + mockMessageHandler.Protected() + .Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny()) + .Returns( + (HttpRequestMessage _, CancellationToken _) => + { + // always return a 200 OK response + var response = new HttpResponseMessage(HttpStatusCode.OK); + + return Task.FromResult(response); + }) + .Verifiable(); + + BuildCreateVersionInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = buildId.ToString(), + FileDirectory = m_TempDirectory, + RemoveOldFiles = removeOldFiles + }; + + await BuildCreateVersionHandler.BuildCreateVersionAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + new HttpClient(mockMessageHandler.Object), + CancellationToken.None); + + mockMessageHandler.Protected() + .Verify( + "SendAsync", + Times.Exactly(expectedSignedUrUploadsRequests), + ItExpr.IsAny(), + ItExpr.IsAny()); + + BuildsApi!.DefaultBuildsClient.Verify( + api => api.CreateNewBuildVersionAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + 0, + CancellationToken.None + ), + Times.Once); + } + + [Test] + public async Task BuildCreateVersionAsync_FileUpload_EmptyDirectory() + { + BuildCreateVersionInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = BuildWithOneFileId.ToString(), + FileDirectory = m_TempDirectoryEmpty, + RemoveOldFiles = false + }; + + await BuildCreateVersionHandler.BuildCreateVersionAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + MockHttpClient!.Object, + CancellationToken.None + ); + + BuildsApi!.DefaultBuildsClient.Verify( + api => api.CreateNewBuildVersionAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + 0, + CancellationToken.None + ), + Times.Never); + } + + [TestCase( + null, + null, + null, + null, + null, + typeof(MissingInputException), + TestName = "Missing file directory" + )] + [TestCase( + null, + "s3://bucket.url", + null, + null, + "/path/to/files", + typeof(CliException), + TestName = "S3 bucket with file build" + )] + [TestCase( + null, + null, + null, + "v1", + "/path/to/files", + typeof(CliException), + TestName = "Container tag with file build" + )] + public void ValidateInput_FileUpload( + string? accessKey, + string? bucketUrl, + string? secretKey, + string? containerTag, + string? fileDirectory, + Type expectedExceptionType + ) + { + var input = new BuildCreateVersionInput + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + AccessKey = accessKey, + BucketUrl = bucketUrl, + SecretKey = secretKey, + ContainerTag = containerTag, + FileDirectory = fileDirectory, + }; + + Assert.Throws(expectedExceptionType, () => BuildCreateVersionHandler.ValidateFileUploadInput(input)); + } + + [Test] + public void BuildCreateVersionAsync_FileUpload_SyncingBuildThrowsException() + { + BuildCreateVersionInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = SyncingBuildId.ToString(), + FileDirectory = m_TempDirectory, + }; + + Assert.ThrowsAsync( + () => BuildCreateVersionHandler.BuildCreateVersionAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + new Mock().Object, + CancellationToken.None + ) + ); + + BuildsApi!.DefaultBuildsClient.Verify( + api => api.CreateNewBuildVersionAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + 0, + CancellationToken.None + ), + Times.Never); + + TestsHelper.VerifyLoggerWasCalled( + MockLogger!, + LogLevel.Critical, + LoggerExtension.ResultEventId, + Times.Never); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionHandlerTests.cs new file mode 100644 index 0000000..de3bd9b --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildCreateVersionHandlerTests.cs @@ -0,0 +1,70 @@ +using System.Net; +using Microsoft.Extensions.Logging; +using Moq; +using Moq.Protected; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +partial class BuildCreateVersionHandlerTests : HandlerCommon +{ + [SetUp] + public new void SetUp() + { + base.SetUp(); + + SetUpTempFiles(); + } + + [Test] + public async Task BuildCreateVersionAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await BuildCreateVersionHandler.BuildCreateVersionAsync( + null!, + MockUnityEnvironment.Object, + null!, + null!, + MockHttpClient!.Object, + mockLoadingIndicator.Object, + CancellationToken.None + ); + + mockLoadingIndicator.Verify( + ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), + Times.Once); + } + + [Test] + public async Task BuildCreateVersionAsync_CallsFetchIdentifierAsync() + { + BuildCreateVersionInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = ValidBuildIdContainer.ToString(), + ContainerTag = ValidContainerTag + }; + + await BuildCreateVersionHandler.BuildCreateVersionAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + MockHttpClient!.Object, + CancellationToken.None); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildDeleteHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildDeleteHandlerTests.cs new file mode 100644 index 0000000..4c1e414 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildDeleteHandlerTests.cs @@ -0,0 +1,130 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class BuildDeleteHandlerTests : HandlerCommon +{ + [Test] + public async Task BuildDeleteAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await BuildDeleteHandler.BuildDeleteAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task BuildDeleteAsync_CallsFetchIdentifierAsync() + { + BuildIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = ValidBuildIdContainer.ToString() + }; + + await BuildDeleteHandler.BuildDeleteAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, null)] + public void BuildDeleteAsync_NullBuildIdThrowsException(string projectId, string environmentName, string buildId) + { + BuildIdInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + BuildId = buildId + }; + + Assert.ThrowsAsync(() => + BuildDeleteHandler.BuildDeleteAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + BuildsApi!.DefaultBuildsClient.Verify(api => api.GetBuildAsync( + It.IsAny(), It.IsAny(), + It.IsAny(), 0, CancellationToken.None + ), Times.Never); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } + + [Test] + public async Task BuildDeleteAsync_CallsDeleteService() + { + BuildIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = ValidBuildIdBucket.ToString() + }; + + await BuildDeleteHandler.BuildDeleteAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + BuildsApi!.DefaultBuildsClient.Verify(api => api.DeleteBuildAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + long.Parse(input.BuildId), null, 0, CancellationToken.None + ), Times.Once); + + // Clear invocations to Mock Environment + MockUnityEnvironment.Invocations.Clear(); + } + + [TestCase(InvalidProjectId, InvalidEnvironmentId, InvalidBuildId)] + [TestCase(ValidProjectId, InvalidEnvironmentId, InvalidBuildId)] + [TestCase(InvalidProjectId, ValidEnvironmentId, InvalidBuildId)] + [TestCase(InvalidProjectId, InvalidEnvironmentId, ValidBuildId)] + public void BuildDeleteAsync_InvalidInputThrowsException(string projectId, string environmentId, string buildId) + { + BuildIdInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = buildId + }; + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(environmentId); + + Assert.ThrowsAsync(() => + BuildDeleteHandler.BuildDeleteAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildGetHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildGetHandlerTests.cs new file mode 100644 index 0000000..36ed7ff --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildGetHandlerTests.cs @@ -0,0 +1,127 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class BuildGetHandlerTests : HandlerCommon +{ + [Test] + public async Task BuildGetAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await BuildGetHandler.BuildGetAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task BuildGetAsync_CallsFetchIdentifierAsync() + { + BuildIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = ValidBuildIdContainer.ToString() + }; + + await BuildGetHandler.BuildGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, null)] + public void BuildGetAsync_NullBuildIdThrowsException(string projectId, string environmentName, string buildId) + { + BuildIdInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + BuildId = buildId + }; + + Assert.ThrowsAsync(() => + BuildGetHandler.BuildGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + BuildsApi!.DefaultBuildsClient.Verify(api => api.GetBuildAsync( + It.IsAny(), It.IsAny(), + It.IsAny(), 0, CancellationToken.None + ), Times.Never); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } + + [Test] + public async Task BuildGetAsync_CallsGetService() + { + BuildIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = ValidBuildIdContainer.ToString() + }; + + await BuildGetHandler.BuildGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + BuildsApi!.DefaultBuildsClient.Verify(api => api.GetBuildAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + long.Parse(input.BuildId), 0, CancellationToken.None + ), Times.Once); + } + + [TestCase(InvalidProjectId, InvalidEnvironmentId, InvalidBuildId)] + [TestCase(ValidProjectId, InvalidEnvironmentId, InvalidBuildId)] + [TestCase(InvalidProjectId, ValidEnvironmentId, InvalidBuildId)] + [TestCase(InvalidProjectId, InvalidEnvironmentId, ValidBuildId)] + public void BuildGetAsync_InvalidInputThrowsException(string projectId, string environmentId, string buildId) + { + BuildIdInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = buildId + }; + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(environmentId); + + Assert.ThrowsAsync(() => + BuildGetHandler.BuildGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildInstallsHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildInstallsHandlerTests.cs new file mode 100644 index 0000000..05fd4bc --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildInstallsHandlerTests.cs @@ -0,0 +1,103 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class BuildInstallsHandlerTests : HandlerCommon +{ + [Test] + public async Task BuildInstallsAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await BuildInstallsHandler.BuildInstallsAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task BuildInstallsAsync_CallsFetchIdentifierAsync() + { + BuildIdInput input = new() + { + BuildId = ValidBuildId, + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName + }; + + await BuildInstallsHandler.BuildInstallsAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, ValidBuildId, + TestName = "ProjectId, EnvId and BuildId valid")] + public async Task BuildInstallsAsync_CallsInstallsService(string projectId, string environmentName, string buildId) + { + BuildIdInput input = new() + { + BuildId = buildId, + CloudProjectId = projectId, + TargetEnvironmentName = environmentName + }; + + await BuildInstallsHandler.BuildInstallsAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + BuildsApi!.DefaultBuildsClient.Verify(api => api.GetBuildInstallsAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + long.Parse(input.BuildId), 0, CancellationToken.None + ), Times.Once); + } + + [TestCase(InvalidProjectId, InvalidEnvironmentId, InvalidBuildId)] + [TestCase(InvalidProjectId, ValidEnvironmentId, ValidBuildId)] + [TestCase(ValidProjectId, InvalidEnvironmentId, ValidBuildId)] + [TestCase(ValidProjectId, ValidEnvironmentId, InvalidBuildId)] + [TestCase(InvalidProjectId, InvalidEnvironmentId, ValidBuildId)] + [TestCase(InvalidProjectId, ValidEnvironmentId, InvalidBuildId)] + [TestCase(ValidProjectId, InvalidEnvironmentId, InvalidBuildId)] + public void BuildInstallsAsync_InvalidInputThrowsException(string projectId, string environmentId, string buildId) + { + BuildIdInput input = new() + { + BuildId = buildId, + CloudProjectId = projectId, + TargetEnvironmentName = environmentId + }; + + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(environmentId); + + Assert.ThrowsAsync(() => + BuildInstallsHandler.BuildInstallsAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildListHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildListHandlerTests.cs new file mode 100644 index 0000000..99fba62 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildListHandlerTests.cs @@ -0,0 +1,95 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Input; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.TestUtils; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class BuildListHandlerTests : HandlerCommon +{ + [Test] + public async Task BuildListAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await BuildListHandler.BuildListAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task BuildListAsync_CallsFetchIdentifierAsync() + { + CommonInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName + }; + + await BuildListHandler.BuildListAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName)] + public async Task BuildListAsync_CallsListService(string projectId, string environmentName) + { + CommonInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName + }; + + await BuildListHandler.BuildListAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + BuildsApi!.DefaultBuildsClient.Verify(api => api.ListBuildsAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + null, null, null, null, null, null, 0, CancellationToken.None + ), Times.Once); + } + + [TestCase(InvalidProjectId, InvalidEnvironmentId)] + [TestCase(ValidProjectId, InvalidEnvironmentId)] + [TestCase(InvalidProjectId, ValidEnvironmentId)] + [TestCase(InvalidProjectId, ValidEnvironmentId)] + public void BuildListAsync_InvalidInputThrowsException(string projectId, string environmentId) + { + CommonInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = ValidEnvironmentName + }; + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(environmentId); + + Assert.ThrowsAsync(() => + BuildListHandler.BuildListAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildUpdateHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildUpdateHandlerTests.cs new file mode 100644 index 0000000..db68476 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/BuildUpdateHandlerTests.cs @@ -0,0 +1,179 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class BuildUpdateHandlerTests : HandlerCommon +{ + [Test] + public async Task BuildUpdateAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await BuildUpdateHandler.BuildUpdateAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task BuildUpdateAsync_CallsFetchIdentifierAsync() + { + BuildUpdateInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = ValidBuildIdContainer.ToString(), + BuildName = ValidBuildName + }; + + await BuildUpdateHandler.BuildUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, null, ValidBuildName)] + public void BuildUpdateAsync_NullBuildIdThrowsException( + string projectId, + string environmentName, + string buildId, + string newBuildName + ) + { + BuildUpdateInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + BuildId = buildId + }; + + Assert.ThrowsAsync(() => + BuildUpdateHandler.BuildUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + BuildsApi!.DefaultBuildsClient.Verify(api => api.GetBuildAsync( + It.IsAny(), It.IsAny(), + It.IsAny(), 0, CancellationToken.None + ), Times.Never); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, ValidBuildIdContainer, ValidBuildName)] + public async Task BuildUpdateAsync_CallsUpdateService( + string projectId, + string environmentName, + long buildId, + string newBuildName + ) + { + BuildUpdateInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + BuildId = buildId.ToString(), + BuildName = newBuildName + }; + + await BuildUpdateHandler.BuildUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + BuildsApi!.DefaultBuildsClient.Verify(api => api.UpdateBuildAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + long.Parse(input.BuildId), new UpdateBuildRequest(input.BuildName), 0, CancellationToken.None + ), Times.Once); + + // Clear invocations to Mock Environment + MockUnityEnvironment.Invocations.Clear(); + } + + [TestCase(InvalidProjectId, InvalidEnvironmentId, InvalidBuildId, ValidBuildName)] + [TestCase(ValidProjectId, InvalidEnvironmentId, InvalidBuildId, ValidBuildName)] + [TestCase(InvalidProjectId, ValidEnvironmentId, InvalidBuildId, ValidBuildName)] + [TestCase(InvalidProjectId, InvalidEnvironmentId, ValidBuildId, ValidBuildName)] + public void BuildUpdateAsync_InvalidInputThrowsException( + string projectId, + string environmentId, + string buildId, + string newBuildName + ) + { + BuildUpdateInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = buildId, + BuildName = newBuildName + }; + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(environmentId); + + Assert.ThrowsAsync(() => + BuildUpdateHandler.BuildUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, ValidBuildId, null)] + public void BuildUpdateAsync_NullBuildNameThrowsException( + string projectId, + string environmentId, + string buildId, + string newBuildName + ) + { + BuildUpdateInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = ValidEnvironmentName, + BuildId = buildId, + BuildName = newBuildName + }; + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(environmentId); + + Assert.ThrowsAsync(() => + BuildUpdateHandler.BuildUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetCreateHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetCreateHandlerTests.cs new file mode 100644 index 0000000..798e81d --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetCreateHandlerTests.cs @@ -0,0 +1,178 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using Region = Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model.Region; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class FleetCreateHandlerTests : HandlerCommon +{ + [Test] + public async Task FleetCreateAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await FleetCreateHandler.FleetCreateAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task FleetCreateAsync_CallsFetchIdentifierAsync() + { + FleetCreateInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + FleetName = ValidFleetName, + OsFamily = FleetCreateRequest.OsFamilyEnum.LINUX, + BuildConfigurations = new[] + { + ValidBuildConfigurationId + }, + Regions = new[] + { + ValidRegionId + } + }; + + + await FleetCreateHandler.FleetCreateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, ValidFleetName, FleetCreateRequest.OsFamilyEnum.LINUX, + new[] + { + ValidBuildConfigurationId + }, + new[] + { + ValidRegionId + } + )] + public async Task FleetCreateAsync_CallsCreateService(string projectId, string environmentName, string fleetName, + FleetCreateRequest.OsFamilyEnum osFamily, long[] buildConfigurations, string[] regions) + { + FleetCreateInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + FleetName = fleetName, + OsFamily = osFamily, + BuildConfigurations = buildConfigurations, + Regions = regions + }; + + await FleetCreateHandler.FleetCreateAsync(input, MockUnityEnvironment.Object, GameServerHostingService!, + MockLogger!.Object, CancellationToken.None); + + var regionList = regions.Select(r => new Region(regionID: new Guid(r))).ToList(); + + var createRequest = new FleetCreateRequest(name: input.FleetName, osFamily: input.OsFamily, + buildConfigurations: buildConfigurations.ToList(), regions: regionList); + + FleetsApi!.DefaultFleetsClient.Verify(api => api.CreateFleetAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + createRequest, 0, CancellationToken.None + ), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, null, FleetCreateRequest.OsFamilyEnum.LINUX, + new[] + { + ValidBuildConfigurationId + }, + new[] + { + ValidRegionId + }, + TestName = "Missing Fleet Name" + )] + [TestCase(ValidProjectId, ValidEnvironmentName, ValidFleetName, null, + new[] + { + ValidBuildConfigurationId + }, + new[] + { + ValidRegionId + }, + TestName = "Missing OS Family" + )] + [TestCase(ValidProjectId, ValidEnvironmentName, ValidFleetName, FleetCreateRequest.OsFamilyEnum.LINUX, + null, + new[] + { + ValidRegionId + }, + TestName = "Missing build configurations" + )] + [TestCase(ValidProjectId, ValidEnvironmentName, ValidFleetName, FleetCreateRequest.OsFamilyEnum.LINUX, + new[] + { + ValidBuildConfigurationId + }, + null, + TestName = "Missing regions" + )] + [TestCase(ValidProjectId, ValidEnvironmentName, ValidFleetName, FleetCreateRequest.OsFamilyEnum.LINUX, + new long[0], + new[] + { + ValidRegionId + }, + TestName = "Empty build configurations" + )] + [TestCase(ValidProjectId, ValidEnvironmentName, ValidFleetName, FleetCreateRequest.OsFamilyEnum.LINUX, + new[] + { + ValidBuildConfigurationId + }, + new string[0], + TestName = "Emty build regions" + )] + public Task FleetCreateAsync_MissingInputThrowsException(string? projectId, string? environmentName, + string? fleetName, + FleetCreateRequest.OsFamilyEnum? osFamily, long[] buildConfigurations, string[] regions) + { + FleetCreateInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + FleetName = fleetName, + OsFamily = osFamily, + BuildConfigurations = buildConfigurations, + Regions = regions + }; + + Assert.ThrowsAsync(() => + FleetCreateHandler.FleetCreateAsync(input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + return Task.CompletedTask; + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetDeleteHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetDeleteHandlerTests.cs new file mode 100644 index 0000000..5b9c1fb --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetDeleteHandlerTests.cs @@ -0,0 +1,125 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class FleetDeleteHandlerTests : HandlerCommon +{ + [Test] + public async Task FleetDeleteAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await FleetDeleteHandler.FleetDeleteAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task FleetDeleteAsync_CallsFetchIdentifierAsync() + { + FleetIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + FleetId = ValidFleetId + }; + + await FleetDeleteHandler.FleetDeleteAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, null)] + public void FleetDeleteAsync_NullFleetIdThrowsException(string projectId, string environmentName, string fleetId) + { + FleetIdInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + FleetId = fleetId + }; + + Assert.ThrowsAsync(() => + FleetDeleteHandler.FleetDeleteAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + FleetsApi!.DefaultFleetsClient.Verify(api => api.GetFleetAsync( + It.IsAny(), It.IsAny(), + It.IsAny(), 0, CancellationToken.None + ), Times.Never); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, ValidFleetId)] + public async Task FleetDeleteAsync_CallsDeleteService(string projectId, string environmentName, string fleetId) + { + FleetIdInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + FleetId = fleetId + }; + + await FleetDeleteHandler.FleetDeleteAsync(input, MockUnityEnvironment.Object, GameServerHostingService!, + MockLogger!.Object, CancellationToken.None); + + FleetsApi!.DefaultFleetsClient.Verify(api => api.DeleteFleetAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + new Guid(input.FleetId), null, 0, CancellationToken.None + ), Times.Once); + + // Clear invocations to Mock Environment + MockUnityEnvironment.Invocations.Clear(); + } + + [TestCase(InvalidProjectId, InvalidEnvironmentId, InvalidFleetId)] + [TestCase(ValidProjectId, InvalidEnvironmentId, InvalidFleetId)] + [TestCase(InvalidProjectId, ValidEnvironmentId, InvalidFleetId)] + [TestCase(InvalidProjectId, InvalidEnvironmentId, InvalidFleetId)] + public void FleetDeleteAsync_InvalidInputThrowsException(string projectId, string environmentId, string fleetId) + { + FleetIdInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = ValidEnvironmentName, + FleetId = fleetId + }; + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(environmentId); + + Assert.ThrowsAsync(() => + FleetDeleteHandler.FleetDeleteAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetGetHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetGetHandlerTests.cs new file mode 100644 index 0000000..0951389 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetGetHandlerTests.cs @@ -0,0 +1,157 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class FleetGetHandlerTests : HandlerCommon +{ + [Test] + public async Task FleetGetAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await FleetGetHandler.FleetGetAsync( + null!, + MockUnityEnvironment.Object, + null!, + null!, + mockLoadingIndicator.Object, + CancellationToken.None); + + mockLoadingIndicator.Verify( + ex => ex.StartLoadingAsync( + It.IsAny(), + It.IsAny>() + ), + Times.Once + ); + } + + [Test] + public async Task FleetGetAsync_CallsFetchIdentifierAsync() + { + FleetIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + FleetId = ValidFleetId + }; + + await FleetGetHandler.FleetGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, null)] + public void FleetGetAsync_NullFleetIdThrowsException(string projectId, string environmentName, string fleetId) + { + FleetIdInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + FleetId = fleetId + }; + + Assert.ThrowsAsync( + () => + FleetGetHandler.FleetGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + FleetsApi!.DefaultFleetsClient.Verify( + api => api.GetFleetAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + 0, + CancellationToken.None + ), + Times.Never); + + TestsHelper.VerifyLoggerWasCalled( + MockLogger!, + LogLevel.Critical, + LoggerExtension.ResultEventId, + Times.Never); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, ValidFleetId)] + public async Task FleetGetAsync_CallsGetService(string projectId, string environmentName, string fleetId) + { + FleetIdInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + FleetId = fleetId + }; + + await FleetGetHandler.FleetGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + FleetsApi!.DefaultFleetsClient.Verify( + api => api.GetFleetAsync( + new Guid(input.CloudProjectId), + new Guid(ValidEnvironmentId), + new Guid(input.FleetId), + 0, + CancellationToken.None + ), + Times.Once); + } + + [TestCase(InvalidProjectId, InvalidEnvironmentId, InvalidFleetId)] + [TestCase(ValidProjectId, InvalidEnvironmentId, InvalidFleetId)] + [TestCase(InvalidProjectId, ValidEnvironmentId, InvalidFleetId)] + [TestCase(InvalidProjectId, InvalidEnvironmentId, ValidFleetId)] + public void FleetGetAsync_InvalidInputThrowsException(string projectId, string environmentId, string fleetId) + { + FleetIdInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = ValidEnvironmentName, + FleetId = fleetId + }; + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(environmentId); + + Assert.ThrowsAsync( + () => + FleetGetHandler.FleetGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled( + MockLogger!, + LogLevel.Critical, + LoggerExtension.ResultEventId, + Times.Never); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetListHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetListHandlerTests.cs new file mode 100644 index 0000000..b5604d1 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetListHandlerTests.cs @@ -0,0 +1,94 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Input; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.TestUtils; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class FleetListHandlerTests : HandlerCommon +{ + [Test] + public async Task FleetListAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await FleetListHandler.FleetListAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task FleetGetAsync_CallsFetchIdentifierAsync() + { + CommonInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName + }; + + await FleetListHandler.FleetListAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName)] + public async Task FleetListAsync_CallsListService(string projectId, string environmentName) + { + CommonInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName + }; + + await FleetListHandler.FleetListAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + FleetsApi!.DefaultFleetsClient.Verify(api => api.ListFleetsAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + 0, CancellationToken.None + ), Times.Once); + } + + [TestCase(InvalidProjectId, InvalidEnvironmentId)] + [TestCase(ValidProjectId, InvalidEnvironmentId)] + [TestCase(InvalidProjectId, ValidEnvironmentId)] + public void FleetListAsync_InvalidInputThrowsException(string projectId, string environmentId) + { + CommonInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = ValidEnvironmentName + }; + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(environmentId); + + Assert.ThrowsAsync(() => + FleetListHandler.FleetListAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetRegionCreateHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetRegionCreateHandlerTests.cs new file mode 100644 index 0000000..ec52577 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetRegionCreateHandlerTests.cs @@ -0,0 +1,110 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class FleetRegionCreateHandlerTests : HandlerCommon +{ + [Test] + public async Task FleetRegionCreateAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await FleetRegionCreateHandler.FleetRegionCreateAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task FleetRegionCreateAsync_CallsFetchIdentifierAsync() + { + FleetRegionCreateInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + FleetId = new Guid(ValidFleetId), + RegionId = new Guid(ValidRegionId), + MaxServers = 2, + MinAvailableServers = 1, + }; + + + await FleetRegionCreateHandler.FleetRegionCreateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, ValidFleetId, ValidRegionId, 1, 2)] + public async Task FleetRegionCreateAsync_CallsCreateService(string projectId, string environmentName, string fleetId, + string regionId, long minAvailableServers, long maxServers) + { + FleetRegionCreateInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + FleetId = new Guid(fleetId), + RegionId = new Guid(regionId), + MinAvailableServers = minAvailableServers, + MaxServers = maxServers, + }; + + await FleetRegionCreateHandler.FleetRegionCreateAsync(input, MockUnityEnvironment.Object, GameServerHostingService!, + MockLogger!.Object, CancellationToken.None); + + var createRequest = new AddRegionRequest(maxServers: maxServers, minAvailableServers: minAvailableServers, + regionID: new Guid(regionId)); + + FleetsApi!.DefaultFleetsClient.Verify(api => api.AddFleetRegionAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + new Guid(fleetId), createRequest, 0, CancellationToken.None + ), Times.Once); + } + + [TestCase(InvalidProjectId, ValidEnvironmentName, null, ValidRegionId, 2, 1, TestName = "Null fleet")] + [TestCase(ValidProjectId, InvalidEnvironmentId, ValidFleetId, null, 2, 1, TestName = "Null region")] + [TestCase(ValidProjectId, ValidEnvironmentName, InvalidFleetId, ValidRegionId, null, 1, TestName = "Null max servers")] + [TestCase(ValidProjectId, ValidEnvironmentName, InvalidFleetId, ValidRegionId, 2, null, TestName = "Null min available servers")] + public Task FleetRegionCreateAsync_InvalidInputThrowsException(string? projectId, string? environmentName, + string? fleetId, string? regionId, long? maxServers, long? minAvailableServers) + { + FleetRegionCreateInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + FleetId = fleetId == null ? null : new Guid(fleetId), + RegionId = regionId == null ? null : new Guid(regionId), + MaxServers = maxServers, + MinAvailableServers = minAvailableServers, + + }; + + Assert.ThrowsAsync(() => + FleetRegionCreateHandler.FleetRegionCreateAsync(input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + return Task.CompletedTask; + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetRegionUpdateHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetRegionUpdateHandlerTests.cs new file mode 100644 index 0000000..c634a70 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetRegionUpdateHandlerTests.cs @@ -0,0 +1,142 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class FleetRegionUpdateHandlerTests : HandlerCommon +{ + [Test] + public async Task FleetRegionUpdateAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await FleetRegionUpdateHandler.FleetRegionUpdateAsync( + null!, + MockUnityEnvironment.Object, + null!, + null!, + mockLoadingIndicator.Object, + CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex.StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task FleetRegionUpdateAsync_CallsFetchIdentifierAsync() + { + FleetRegionUpdateInput input = new() + { + CloudProjectId = ValidProjectId, + FleetId = Guid.Parse(ValidFleetId), + RegionId = Guid.Parse(ValidRegionId), + DeleteTtl = 120, + DisabledDeleteTtl = 60, + MaxServers = 3, + MinAvailableServers = 3, + ScalingEnabled = false, + ShutdownTtl = 180 + }; + + await FleetRegionUpdateHandler.FleetRegionUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidFleetId, ValidRegionId, 120, 60, 3, 3, false, 180)] + public async Task FleetRegionUpdateAsync_CallsUpdateService(string projectId, string fleetId, string regionId, long deleteTtl, + long disabledDeleteTtl, long maxServers, long minAvailableServers, bool scalingEnabled, long shutdownTtl) + { + FleetRegionUpdateInput input = new() + { + CloudProjectId = projectId, + FleetId = Guid.Parse(fleetId), + RegionId = Guid.Parse(regionId), + DeleteTtl = deleteTtl, + DisabledDeleteTtl = disabledDeleteTtl, + MaxServers = maxServers, + MinAvailableServers = minAvailableServers, + ScalingEnabled = scalingEnabled, + ShutdownTtl = shutdownTtl + }; + + await FleetRegionUpdateHandler.FleetRegionUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + var updateRegionRequest = new UpdateRegionRequest( + deleteTtl, + disabledDeleteTtl, + maxServers, + minAvailableServers, + scalingEnabled, + shutdownTtl + ); + + FleetsApi!.DefaultFleetsClient.Verify(api => api.UpdateFleetRegionAsync( + new Guid(projectId), new Guid(ValidEnvironmentId), + new Guid(fleetId), new Guid(regionId), updateRegionRequest, 0, CancellationToken.None + ), Times.Once); + } + + [TestCase(null, ValidFleetId, ValidRegionId, 120, 60, 3, 3, false, 180, typeof(ArgumentNullException), TestName = "Null Project Id throws ArgumentNullException")] + [TestCase(InvalidProjectId, ValidFleetId, ValidRegionId, 120, 60, 3, 3, false, 180, typeof(HttpRequestException), TestName = "Invalid Project Id throws HttpRequestException")] + [TestCase(ValidProjectId, null, ValidRegionId, 120, 60, 3, 3, false, 180, typeof(MissingInputException), TestName = "Null Fleet Id throws ArgumentNullException")] + [TestCase(ValidProjectId, InvalidFleetId, ValidRegionId, 120, 60, 3, 3, false, 180, typeof(HttpRequestException), TestName = "Invalid Fleet Id throws HttpRequestException")] + [TestCase(ValidProjectId, ValidFleetId, null, 120, 60, 3, 3, false, 180, typeof(MissingInputException), TestName = "Null Region Id throws MissingInputException")] + [TestCase(ValidProjectId, ValidFleetId, InvalidRegionId, 120, 60, 3, 3, false, 180, typeof(HttpRequestException), TestName = "Invalid Region Id throws HttpRequestException")] + [TestCase(ValidProjectId, ValidFleetId, InvalidRegionId, 120, 60, null, 3, false, 180, typeof(MissingInputException), TestName = "Null Max Servers throws MissingInputException")] + [TestCase(ValidProjectId, ValidFleetId, InvalidRegionId, 120, 60, 3, null, false, 180, typeof(MissingInputException), TestName = "Null Min Available Servers throws MissingInputException")] + [TestCase(ValidProjectId, ValidFleetId, InvalidRegionId, 120, 60, 3, 3, null, 180, typeof(MissingInputException), TestName = "Null Scaling Enabled throws MissingInputException")] + [TestCase(ValidProjectId, ValidFleetId, InvalidRegionId, null, 60, 3, 3, false, 180, typeof(MissingInputException), TestName = "Null Delete Ttl throws MissingInputException")] + [TestCase(ValidProjectId, ValidFleetId, InvalidRegionId, 120, null, 3, 3, null, 180, typeof(MissingInputException), TestName = "Null Disabled Delete Ttl throws MissingInputException")] + [TestCase(ValidProjectId, ValidFleetId, InvalidRegionId, 120, 60, 3, 3, false, null, typeof(MissingInputException), TestName = "Null Shutdown Ttl throws MissingInputException")] + public Task FleetRegionUpdateAsync_InvalidInputThrowsException(string? projectId, string? fleetId, string? regionId, + long? deleteTtl, long? disabledDeleteTtl, long? maxServers, long? minAvailableServers, bool? scalingEnabled, + long? shutdownTtl, Type exceptionType) + { + FleetRegionUpdateInput input = new() + { + CloudProjectId = projectId, + FleetId = fleetId == null ? null : new Guid(fleetId), + RegionId = regionId == null ? null : new Guid(regionId), + DeleteTtl = deleteTtl, + DisabledDeleteTtl = disabledDeleteTtl, + MaxServers = maxServers, + MinAvailableServers = minAvailableServers, + ScalingEnabled = scalingEnabled, + ShutdownTtl = shutdownTtl + }; + + Assert.ThrowsAsync(exceptionType, () => + FleetRegionUpdateHandler.FleetRegionUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + return Task.CompletedTask; + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetUpdateHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetUpdateHandlerTests.cs new file mode 100644 index 0000000..580d4b6 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/FleetUpdateHandlerTests.cs @@ -0,0 +1,230 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class FleetUpdateHandlerTests : HandlerCommon +{ + [Test] + public async Task FleetUpdateAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await FleetUpdateHandler.FleetUpdateAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task FleetUpdateAsync_CallsFetchIdentifierAsync() + { + FleetUpdateInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + FleetId = ValidFleetId, + AllocTtl = 0, + BuildConfigs = new List(), + DisabledDeleteTtl = 0, + ShutdownTtl = 0, + DeleteTtl = 0 + }; + + await FleetUpdateHandler.FleetUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, null)] + public void FleetUpdateAsync_NullFleetIdThrowsException( + string projectId, + string environmentName, + string fleetId + ) + { + FleetUpdateInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + FleetId = fleetId + }; + + Assert.ThrowsAsync(() => + FleetUpdateHandler.FleetUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + FleetsApi!.DefaultFleetsClient.Verify(api => api.UpdateFleetAsync( + It.IsAny(), It.IsAny(), + It.IsAny(), null, 0, CancellationToken.None + ), Times.Never); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, ValidFleetId)] + public async Task FleetUpdateAsync_CallsUpdateService( + string projectId, + string environmentName, + string fleetId + ) + { + FleetUpdateInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + FleetId = ValidFleetId, + Name = ValidFleetName, + AllocTtl = 0, + DeleteTtl = 0, + BuildConfigs = new List() { 1 }, + DisabledDeleteTtl = 0, + ShutdownTtl = 0 + }; + + await FleetUpdateHandler.FleetUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + FleetUpdateRequest req = new FleetUpdateRequest(name: input.Name, buildConfigurations: input.BuildConfigs); + + FleetsApi!.DefaultFleetsClient.Verify(api => api.UpdateFleetAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + new Guid(fleetId), req, 0, CancellationToken.None + ), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, ValidFleetId)] + public async Task FleetUpdateAsync_CallsGetFleetIfNoNameOrBuildConfigs( + string projectId, + string environmentName, + string fleetId + ) + { + FleetUpdateInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + FleetId = ValidFleetId, + Name = ValidFleetName, + AllocTtl = 0, + DeleteTtl = 0, + BuildConfigs = new List() { 1 }, + DisabledDeleteTtl = 0, + ShutdownTtl = 0 + }; + + await FleetUpdateHandler.FleetUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + FleetUpdateRequest expected = new FleetUpdateRequest(name: input.Name, buildConfigurations: input.BuildConfigs); + + FleetsApi!.DefaultFleetsClient.Verify(api => api.UpdateFleetAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + new Guid(fleetId), expected, 0, CancellationToken.None + ), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, InvalidFleetId)] + public void FleetUpdateAsync_InvalidFleetIdThrowsException( + string projectId, + string environmentName, + string fleetId + ) + { + FleetUpdateInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + FleetId = fleetId, + Name = ValidFleetName, + AllocTtl = 0, + DeleteTtl = 0, + BuildConfigs = new List(), + DisabledDeleteTtl = 0, + ShutdownTtl = 0 + }; + + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(ValidEnvironmentId); + + Assert.ThrowsAsync(() => + FleetUpdateHandler.FleetUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, ValidFleetId)] + public async Task FleetUpdateAsync_FillNameAndBuildConfigsIfNotProvided( + string projectId, + string environmentName, + string fleetId + ) + { + FleetUpdateInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + FleetId = fleetId, + Name = null, + AllocTtl = 0, + DeleteTtl = 0, + BuildConfigs = new List(), + DisabledDeleteTtl = 0, + ShutdownTtl = 0 + }; + + await FleetUpdateHandler.FleetUpdateAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + FleetUpdateRequest expected = + new FleetUpdateRequest(name: ValidFleetName, buildConfigurations: new List() { 1 }); + + FleetsApi!.DefaultFleetsClient.Verify(api => api.UpdateFleetAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + new Guid(fleetId), expected, 0, CancellationToken.None + ), Times.Once); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/HandlerCommon.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/HandlerCommon.cs new file mode 100644 index 0000000..7fed7ac --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/HandlerCommon.cs @@ -0,0 +1,101 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Cli.GameServerHosting.UnitTest.Mocks; +using Unity.Services.Cli.ServiceAccountAuthentication; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +class HandlerCommon +{ + protected static Mock? MockLogger; + protected static Mock? MockHttpClient; + static Mock? s_AuthenticationServiceObject; + protected static readonly Mock MockUnityEnvironment = new(); + internal static GameServerHostingBuildsApiV1Mock? BuildsApi; + internal static GameServerHostingFleetsApiV1Mock? FleetsApi; + internal static GameServerHostingServersApiV1Mock? ServersApi; + internal static GameServerHostingBuildConfigurationsApiV1Mock? BuildConfigurationsApi; + protected static GameServerHostingService? GameServerHostingService; + + [SetUp] + public void SetUp() + { + MockLogger = new Mock(); + MockHttpClient = new Mock(); + + s_AuthenticationServiceObject = new Mock(); + s_AuthenticationServiceObject.Setup(a => a.GetAccessTokenAsync(CancellationToken.None)) + .ReturnsAsync(TestAccessToken); + + BuildsApi = new GameServerHostingBuildsApiV1Mock + { + ValidProjects = new List + { + Guid.Parse(ValidProjectId) + }, + ValidEnvironments = new List + { + Guid.Parse(ValidEnvironmentId) + } + }; + BuildsApi.SetUp(); + + FleetsApi = new GameServerHostingFleetsApiV1Mock + { + ValidProjects = new List + { + Guid.Parse(ValidProjectId) + }, + ValidEnvironments = new List + { + Guid.Parse(ValidEnvironmentId) + } + }; + FleetsApi.SetUp(); + + ServersApi = new GameServerHostingServersApiV1Mock + { + ValidProjects = new List + { + Guid.Parse(ValidProjectId) + }, + ValidEnvironments = new List + { + Guid.Parse(ValidEnvironmentId) + } + }; + ServersApi.SetUp(); + + BuildConfigurationsApi = new GameServerHostingBuildConfigurationsApiV1Mock + { + ValidProjects = new List + { + Guid.Parse(ValidProjectId) + }, + ValidEnvironments = new List + { + Guid.Parse(ValidEnvironmentId) + } + }; + BuildConfigurationsApi.SetUp(); + + GameServerHostingService = new GameServerHostingService( + s_AuthenticationServiceObject.Object, + BuildsApi.DefaultBuildsClient.Object, + BuildConfigurationsApi.DefaultBuildConfigurationsClient.Object, + FleetsApi.DefaultFleetsClient.Object, + ServersApi.DefaultServersClient.Object + ); + + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(ValidEnvironmentId); + } + + [TearDown] + public void TearDown() + { + // Clear invocations to Mock Environment + MockUnityEnvironment.Invocations.Clear(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/RegionAvailableHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/RegionAvailableHandlerTests.cs new file mode 100644 index 0000000..5160d7a --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/RegionAvailableHandlerTests.cs @@ -0,0 +1,99 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class RegionAvailableHandlerTests : HandlerCommon +{ + [Test] + public async Task RegionAvailableAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await RegionAvailableHandler.RegionAvailableAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task BuildListAsync_CallsFetchIdentifierAsync() + { + FleetIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + FleetId = ValidFleetId + }; + + await RegionAvailableHandler.RegionAvailableAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, ValidFleetId)] + public async Task RegionAvailableAsync_CallsListService(string projectId, string environmentName, string fleetId) + { + FleetIdInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + FleetId = fleetId + }; + + await RegionAvailableHandler.RegionAvailableAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + FleetsApi!.DefaultFleetsClient.Verify(api => api.GetAvailableFleetRegionsAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + new Guid(ValidFleetId), 0, CancellationToken.None + ), Times.Once); + } + + [TestCase(InvalidProjectId, InvalidEnvironmentId, ValidFleetId)] + [TestCase(ValidProjectId, InvalidEnvironmentId, ValidFleetId)] + [TestCase(InvalidProjectId, ValidEnvironmentId, ValidFleetId)] + [TestCase(InvalidProjectId, ValidEnvironmentId, ValidFleetId)] + [TestCase(ValidProjectId, ValidProjectId, InvalidFleetId)] + public void RegionAvailableAsync_InvalidInputThrowsException(string projectId, string environmentId, string fleetId) + { + FleetIdInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = ValidEnvironmentName, + FleetId = fleetId + }; + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(environmentId); + + Assert.ThrowsAsync(() => + RegionAvailableHandler.RegionAvailableAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/RegionTemplatesHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/RegionTemplatesHandlerTests.cs new file mode 100644 index 0000000..65ecef5 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/RegionTemplatesHandlerTests.cs @@ -0,0 +1,95 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Input; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.TestUtils; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class RegionTemplatesHandlerTests : HandlerCommon +{ + [Test] + public async Task RegionListAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await RegionTemplatesHandler.RegionTemplatesAsync(null!, MockUnityEnvironment.Object, null!, null!, + mockLoadingIndicator.Object, CancellationToken.None); + + mockLoadingIndicator.Verify(ex => ex + .StartLoadingAsync(It.IsAny(), It.IsAny>()), Times.Once); + } + + [Test] + public async Task RegionGetAsync_CallsFetchIdentifierAsync() + { + CommonInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName + }; + + await RegionTemplatesHandler.RegionTemplatesAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase(ValidProjectId, ValidEnvironmentName)] + public async Task RegionListAsync_CallsListService(string projectId, string environmentName) + { + CommonInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName + }; + + await RegionTemplatesHandler.RegionTemplatesAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + FleetsApi!.DefaultFleetsClient.Verify(api => api.ListTemplateFleetRegionsAsync( + new Guid(input.CloudProjectId), new Guid(ValidEnvironmentId), + 0, CancellationToken.None + ), Times.Once); + } + + [TestCase(InvalidProjectId, InvalidEnvironmentId)] + [TestCase(ValidProjectId, InvalidEnvironmentId)] + [TestCase(InvalidProjectId, ValidEnvironmentId)] + [TestCase(InvalidProjectId, ValidEnvironmentId)] + public void RegionListAsync_InvalidInputThrowsException(string projectId, string environmentId) + { + CommonInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = ValidEnvironmentName + }; + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(environmentId); + + Assert.ThrowsAsync(() => + RegionTemplatesHandler.RegionTemplatesAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled(MockLogger!, LogLevel.Critical, LoggerExtension.ResultEventId, Times.Never); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/ServerGetHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/ServerGetHandlerTests.cs new file mode 100644 index 0000000..c9f36dc --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/ServerGetHandlerTests.cs @@ -0,0 +1,156 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.TestUtils; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class ServerGetHandlerTests : HandlerCommon +{ + [Test] + public async Task ServerGetAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + await ServerGetHandler.ServerGetAsync( + null!, + MockUnityEnvironment.Object, + null!, + null!, + mockLoadingIndicator.Object, + CancellationToken.None); + + mockLoadingIndicator.Verify( + ex => ex.StartLoadingAsync( + It.IsAny(), + It.IsAny>() + ), + Times.Once + ); + } + + [Test] + public async Task ServerGetAsync_CallsFetchIdentifierAsync() + { + ServerIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + ServerId = ValidServerId.ToString() + }; + + await ServerGetHandler.ServerGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [Test] + public void ServerGetAsync_NullServerIdThrowsException() + { + ServerIdInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + ServerId = null + }; + + Assert.ThrowsAsync( + () => + ServerGetHandler.ServerGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + ServersApi!.DefaultServersClient.Verify( + api => api.GetServerAsync( + It.IsAny(), + It.IsAny(), + 0, + 0, + CancellationToken.None + ), + Times.Never); + + TestsHelper.VerifyLoggerWasCalled( + MockLogger!, + LogLevel.Critical, + LoggerExtension.ResultEventId, + Times.Never); + } + + [TestCase(ValidProjectId, ValidEnvironmentName, ValidServerId)] + public async Task ServerGetAsync_CallsGetService(string projectId, string environmentName, long serverId) + { + ServerIdInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = environmentName, + ServerId = serverId.ToString() + }; + + await ServerGetHandler.ServerGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + ServersApi!.DefaultServersClient.Verify( + api => api.GetServerAsync( + It.IsAny(), + It.IsAny(), + 0, + 0, + CancellationToken.None + ), + Times.Never); + } + + [TestCase(InvalidProjectId, InvalidEnvironmentId, InvalidServerId)] + [TestCase(ValidProjectId, InvalidEnvironmentId, InvalidServerId)] + [TestCase(InvalidProjectId, ValidEnvironmentId, InvalidServerId)] + [TestCase(InvalidProjectId, InvalidEnvironmentId, ValidServerId)] + public void ServerGetAsync_InvalidInputThrowsException(string projectId, string environmentId, long serverId) + { + ServerIdInput input = new() + { + CloudProjectId = projectId, + TargetEnvironmentName = ValidEnvironmentName, + ServerId = serverId.ToString() + }; + MockUnityEnvironment.Setup(ex => ex.FetchIdentifierAsync(CancellationToken.None)).ReturnsAsync(environmentId); + + Assert.ThrowsAsync( + () => + ServerGetHandler.ServerGetAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ) + ); + + TestsHelper.VerifyLoggerWasCalled( + MockLogger!, + LogLevel.Critical, + LoggerExtension.ResultEventId, + Times.Never); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/ServerListHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/ServerListHandlerTests.cs new file mode 100644 index 0000000..83b0b53 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Handlers/ServerListHandlerTests.cs @@ -0,0 +1,134 @@ +using Moq; +using Spectre.Console; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Handlers; + +[TestFixture] +class ServerListHandlerTests : HandlerCommon +{ + [Test] + public async Task ServerListAsync_CallsLoadingIndicatorStartLoading() + { + var mockLoadingIndicator = new Mock(); + + await ServerListHandler.ServerListAsync( + null!, + MockUnityEnvironment.Object, + null!, + null!, + mockLoadingIndicator.Object, + CancellationToken.None); + + mockLoadingIndicator.Verify( + ex => ex.StartLoadingAsync( + It.IsAny(), + It.IsAny>() + ), + Times.Once + ); + } + + [Test] + public async Task ServerListAsync_CallsFetchIdentifierAsync() + { + ServerListInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName + }; + + await ServerListHandler.ServerListAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + MockUnityEnvironment.Verify(ex => ex.FetchIdentifierAsync(CancellationToken.None), Times.Once); + } + + [TestCase( + null, + null, + null, + null)] + [TestCase( + InvalidFleetId, + ValidBuildConfigurationId, + ValidServerId, + Server.StatusEnum.ONLINE)] + [TestCase( + ValidFleetId, + InvalidBuildConfigurationId, + ValidServerId, + Server.StatusEnum.ONLINE)] + [TestCase( + ValidFleetId, + ValidBuildConfigurationId, + InvalidServerId, + Server.StatusEnum.ONLINE)] + [TestCase( + ValidFleetId, + ValidBuildConfigurationId, + ValidServerId, + Server.StatusEnum.READY)] + public async Task ServerListAsync_CallsListService( + string? fleetId, + long? buildConfigurationId, + long? partial, + Server.StatusEnum? status + ) + { + Guid? fleetGuid = fleetId == null ? null : new Guid(fleetId); + + var buildConfigurationIdString = buildConfigurationId == null ? null : buildConfigurationId.ToString(); + + var partialString = partial == null ? null : partial.ToString(); + + var statusString = status == null ? null : status.ToString(); + + ServerListInput input = new() + { + CloudProjectId = ValidProjectId, + TargetEnvironmentName = ValidEnvironmentName, + FleetId = fleetId, + BuildConfigurationId = buildConfigurationIdString, + Partial = partialString, + Status = statusString + }; + + await ServerListHandler.ServerListAsync( + input, + MockUnityEnvironment.Object, + GameServerHostingService!, + MockLogger!.Object, + CancellationToken.None + ); + + ServersApi!.DefaultServersClient.Verify( + api => api.ListServersAsync( + new Guid(input.CloudProjectId), + new Guid(ValidEnvironmentId), + null, + null, + null, + null, + null, + fleetGuid, + null, + buildConfigurationIdString, + null, + partialString, + statusString, + 0, + CancellationToken.None + ), + Times.Once + ); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildConfigurationIdInputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildConfigurationIdInputTests.cs new file mode 100644 index 0000000..dec8628 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildConfigurationIdInputTests.cs @@ -0,0 +1,14 @@ +using System.CommandLine; +using Unity.Services.Cli.GameServerHosting.Input; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Input; + +public class BuildConfigurationIdInputTests +{ + [TestCase("alphaString", false)] + [TestCase("123456", true)] + public void Validate_WithValidUUIDInput_ReturnsTrue(string buildConfiguratinId, bool validates) + { + Assert.That(BuildConfigurationIdInput.BuildConfigurationIdArgument.Parse(buildConfiguratinId).Errors, validates ? Is.Empty : Is.Not.Empty); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildConfigurationListInputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildConfigurationListInputTests.cs new file mode 100644 index 0000000..f5767f0 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildConfigurationListInputTests.cs @@ -0,0 +1,19 @@ +using System.CommandLine; +using Unity.Services.Cli.GameServerHosting.Input; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Input; + +public class BuildConfigurationInputTests +{ + [TestCase(InvalidUuid, false)] + [TestCase(ValidFleetId, true)] + public void Validate_WithValidUUIDInput_ReturnsTrue(string fleetId, bool validates) + { + var arg = new[] + { + BuildConfigurationListInput.FleetIdKey, + fleetId, + }; + Assert.That(BuildConfigurationListInput.FleetIdOption.Parse(arg).Errors, validates ? Is.Empty : Is.Not.Empty); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildCreateInputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildCreateInputTests.cs new file mode 100644 index 0000000..aae8105 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildCreateInputTests.cs @@ -0,0 +1,47 @@ +using System.CommandLine; +using Unity.Services.Cli.GameServerHosting.Input; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Input; + +public class BuildCreateInputTests +{ + [TestCase(new[] + { + BuildCreateInput.TypeKey, + "invalid" + }, false)] + [TestCase(new[] + { + BuildCreateInput.TypeKey, + "FILEUPLOAD" + }, true)] + [TestCase(new[] + { + BuildCreateInput.TypeKey, + "CONTAINER" + }, true)] + [TestCase(new[] + { + BuildCreateInput.TypeKey, + "S3" + }, true)] + public void Validate_WithValidUUIDInput_ReturnsTrue(string[] buildType, bool validates) + { + Assert.That(BuildCreateInput.BuildTypeOption.Parse(buildType).Errors, validates ? Is.Empty : Is.Not.Empty); + } + + [TestCase(new[] + { + BuildCreateInput.OsFamilyKey, + "invalid" + }, false)] + [TestCase(new[] + { + BuildCreateInput.OsFamilyKey, + "LINUX" + }, true)] + public void Validate_WithValidOSFamily_ReturnsTrue(string[] osFamily, bool validates) + { + Assert.That(BuildCreateInput.BuildOsFamilyOption.Parse(osFamily).Errors, validates ? Is.Empty : Is.Not.Empty); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildIdInputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildIdInputTests.cs new file mode 100644 index 0000000..88f5d6d --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/BuildIdInputTests.cs @@ -0,0 +1,14 @@ +using System.CommandLine; +using Unity.Services.Cli.GameServerHosting.Input; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Input; + +public class BuildIdInputTests +{ + [TestCase("alphaString", false)] + [TestCase("123456", true)] + public void Validate_WithValidUUIDInput_ReturnsTrue(string buildId, bool validates) + { + Assert.That(BuildIdInput.BuildIdArgument.Parse(buildId).Errors, validates ? Is.Empty : Is.Not.Empty); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/FleetCreateInputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/FleetCreateInputTests.cs new file mode 100644 index 0000000..736cd43 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/FleetCreateInputTests.cs @@ -0,0 +1,39 @@ +using System.CommandLine; +using Unity.Services.Cli.GameServerHosting.Input; + + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Input; + +[TestFixture] +public class FleetCreateInputTests +{ + [TestCase(new[] + { + FleetCreateInput.RegionsKey, + InvalidUuid + }, false)] + [TestCase(new[] + { + FleetCreateInput.RegionsKey, + ValidRegionId + }, true)] + public void Validate_WithValidUUIDInput_ReturnsTrue(string[] regions, bool validates) + { + Assert.That(FleetCreateInput.FleetRegionsOption.Parse(regions).Errors, validates ? Is.Empty : Is.Not.Empty); + } + + [TestCase(new[] + { + FleetCreateInput.OsFamilyKey, + "invalid" + }, false)] + [TestCase(new[] + { + FleetCreateInput.OsFamilyKey, + "LINUX" + }, true)] + public void Validate_WithValidOSFamily_ReturnsTrue(string[] osFamily, bool validates) + { + Assert.That(FleetCreateInput.FleetOsFamilyOption.Parse(osFamily).Errors, validates ? Is.Empty : Is.Not.Empty); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/FleetIdInputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/FleetIdInputTests.cs new file mode 100644 index 0000000..c78134f --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/FleetIdInputTests.cs @@ -0,0 +1,14 @@ +using System.CommandLine; +using Unity.Services.Cli.GameServerHosting.Input; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Input; + +public class FleetIdInputTests +{ + [TestCase(InvalidUuid, false)] + [TestCase(ValidFleetId,true)] + public void Validate_WithValidUUIDInput_ReturnsTrue(string regions, bool validates) + { + Assert.That(FleetIdInput.FleetIdArgument.Parse(regions).Errors, validates ? Is.Empty : Is.Not.Empty); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/ServerIdInputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/ServerIdInputTests.cs new file mode 100644 index 0000000..06c051c --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/ServerIdInputTests.cs @@ -0,0 +1,14 @@ +using System.CommandLine; +using Unity.Services.Cli.GameServerHosting.Input; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Input; + +public class ServerIdInputTests +{ + [TestCase("nan", false)] + [TestCase("666", true)] + public void Validate_WithValidServerIdInput_ReturnsTrue(string serverId, bool validates) + { + Assert.That(ServerIdInput.ServerIdArgument.Parse(serverId).Errors, validates ? Is.Empty : Is.Not.Empty); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/ServerListInputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/ServerListInputTests.cs new file mode 100644 index 0000000..746bab9 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Input/ServerListInputTests.cs @@ -0,0 +1,43 @@ +using System.CommandLine; +using Unity.Services.Cli.GameServerHosting.Input; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Input; + +public class ServerListInputTests +{ + [TestCase(InvalidUuid, false)] + [TestCase(ValidFleetId, true)] + public void Validate_WithValidUUIDInput_ReturnsTrue(string id, bool validates) + { + var arg = new[] + { + ServerListInput.FleetIdKey, + id + }; + Assert.That(ServerListInput.FleetIdOption.Parse(arg).Errors, validates ? Is.Empty : Is.Not.Empty); + } + + [TestCase("invalid", false)] + [TestCase("1", true)] + public void Validate_WithValidBuildConfigurationIdInput_ReturnsTrue(string id, bool validates) + { + var arg = new[] + { + ServerListInput.BuildConfigurationIdKey, + id + }; + Assert.That(ServerListInput.BuildConfigurationIdOption.Parse(arg).Errors, validates ? Is.Empty : Is.Not.Empty); + } + + [TestCase("invalid", false)] + [TestCase("ONLINE", true)] + public void Validate_WithValidStatusInput_ReturnsTrue(string status, bool validates) + { + var arg = new[] + { + ServerListInput.StatusKey, + status + }; + Assert.That(ServerListInput.StatusOption.Parse(arg).Errors, validates ? Is.Empty : Is.Not.Empty); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingBuildApiV1AsyncMock.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingBuildApiV1AsyncMock.cs new file mode 100644 index 0000000..7f82133 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingBuildApiV1AsyncMock.cs @@ -0,0 +1,502 @@ +using Moq; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Client; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Mocks; + +class GameServerHostingBuildsApiV1Mock +{ + readonly List m_BuildIdsThatCanBeUpdatedCreatedOrDeleted = new() + { + BuildWithOneFileId.ToString(), + BuildWithTwoFilesId.ToString() + }; + + readonly List m_DeletableFiles = new() + { + BuildWithOneToBeDeletedFileFileName + }; + + readonly Dictionary m_FilesByBuildId = new() + { + { + BuildFilesRequestMockKey(BuildWithOneFileId.ToString(), 100, 0), new BuildFilesList( + 100, + 0, + new List + { + new("", BuildWithOneFileFileName) + } + ) + }, + { + BuildFilesRequestMockKey(BuildWithTwoFilesId.ToString(), 100, 0), new BuildFilesList( + 100, + 0, + new List + { + new("", BuildWithOneFileFileName), + new("", BuildWithOneToBeDeletedFileFileName) + } + ) + }, + { + BuildFilesRequestMockKey(BuildWithTwoFilesId.ToString(), 1, 0), new BuildFilesList( + 1, + 0, + new List + { + new("", BuildWithOneFileFileName) + } + ) + }, + { + BuildFilesRequestMockKey(BuildWithTwoFilesId.ToString(), 1, 1), new BuildFilesList( + 1, + 1, + new List + { + new("", BuildWithOneToBeDeletedFileFileName) + } + ) + }, + { + BuildFilesRequestMockKey(BuildWithTwoFilesId.ToString(), 1, 2), new BuildFilesList( + 1, + 2, + new List()) + }, + { + BuildFilesRequestMockKey(BuildWithOneFileId.ToString(), 2, 0), new BuildFilesList( + 2, + 0, + new List + { + new("", BuildWithOneFileFileName) + } + ) + } + }; + + readonly List m_TestBuildInstalls = new() + { + new BuildListInner1( + new CCDDetails(Guid.Parse(ValidBucketId), Guid.Parse(ValidReleaseId)), + completedMachines: 1, + container: new ContainerImage("tag"), + failures: new List + { + new(1234, "failure", DateTime.Now) + }, + fleetName: "fleet name", + pendingMachines: 1, + regions: new List + { + new(1, 1, 1, "region name") + }), + new BuildListInner1( + new CCDDetails(Guid.Parse(ValidBucketId), Guid.Parse(ValidReleaseId)), + completedMachines: 3, + container: new ContainerImage("tag"), + failures: new List + { + new(3456, "failure", DateTime.Now) + }, + fleetName: "another fleet name", + pendingMachines: 2, + regions: new List + { + new(3, 1, 2, "another region name") + }) + }; + + readonly List m_TestBuilds = new() + { + new CreateBuild200Response( + ValidBuildIdBucket, + "build2-bucket-build", + CreateBuild200Response.BuildTypeEnum.S3, + s3: new AmazonS3Details("s3://bucket-name"), + osFamily: CreateBuild200Response.OsFamilyEnum.LINUX, + syncStatus: CreateBuild200Response.SyncStatusEnum.SYNCED, + updated: new DateTime(2022, 10, 11)), + new CreateBuild200Response( + ValidBuildIdContainer, + "build1-container-build", + CreateBuild200Response.BuildTypeEnum.CONTAINER, + container: new ContainerImage(ValidContainerTag), + osFamily: CreateBuild200Response.OsFamilyEnum.LINUX, + syncStatus: CreateBuild200Response.SyncStatusEnum.SYNCED, + updated: new DateTime(2022, 10, 11)), + new CreateBuild200Response( + ValidBuildIdFileUpload, + "build3-file-upload-build", + CreateBuild200Response.BuildTypeEnum.FILEUPLOAD, + ccd: new CCDDetails(Guid.Parse(ValidBucketId), Guid.Parse(ValidReleaseId)), + osFamily: CreateBuild200Response.OsFamilyEnum.LINUX, + syncStatus: CreateBuild200Response.SyncStatusEnum.SYNCED, + updated: new DateTime(2022, 10, 11)), + new CreateBuild200Response( + BuildWithOneFileId, + "Build3 (Build with one file test)", + CreateBuild200Response.BuildTypeEnum.FILEUPLOAD, + new CCDDetails( + new Guid(ValidBucketId), + new Guid(ValidReleaseId)), + osFamily: CreateBuild200Response.OsFamilyEnum.LINUX, + syncStatus: CreateBuild200Response.SyncStatusEnum.SYNCED, + updated: new DateTime(2022, 10, 11)), + new CreateBuild200Response( + BuildWithTwoFilesId, + "Build3 (Build with one file test)", + CreateBuild200Response.BuildTypeEnum.FILEUPLOAD, + new CCDDetails( + new Guid(ValidBucketId), + new Guid(ValidReleaseId)), + osFamily: CreateBuild200Response.OsFamilyEnum.LINUX, + syncStatus: CreateBuild200Response.SyncStatusEnum.SYNCED, + updated: new DateTime(2022, 10, 11)), + new CreateBuild200Response( + SyncingBuildId, + "Syncing Build", + CreateBuild200Response.BuildTypeEnum.FILEUPLOAD, + ccd: new CCDDetails(), + osFamily: CreateBuild200Response.OsFamilyEnum.LINUX, + syncStatus: CreateBuild200Response.SyncStatusEnum.SYNCING, + updated: new DateTime(2022, 10, 11)), + }; + + readonly List m_TestListBuilds = new() + { + new BuildListInner( + 1, + 11, + "Build1", + ccd: new CCDDetails( + new Guid(ValidBucketId), + new Guid(ValidReleaseId)), + osFamily: BuildListInner.OsFamilyEnum.LINUX, + syncStatus: BuildListInner.SyncStatusEnum.SYNCED, + updated: new DateTime(2022, 10, 11)), + new BuildListInner( + 2, + 22, + "Build2", + container: new ContainerImage("v1"), + osFamily: BuildListInner.OsFamilyEnum.LINUX, + syncStatus: BuildListInner.SyncStatusEnum.SYNCED, + updated: new DateTime(2022, 10, 11)) + }; + + public Mock DefaultBuildsClient = new(); + + public List? ValidEnvironments; + public List? ValidProjects; + + + /// + /// Sets up an extremely lightweight implementation of GameServerHosting Build service logic in order to mock each API + /// response. + /// + public void SetUp() + { + DefaultBuildsClient = new Mock(); + + DefaultBuildsClient.Setup(a => a.Configuration) + .Returns(new Configuration()); + + DefaultBuildsClient.Setup(a => + a.CreateBuildAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // build + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, CreateBuildRequest req, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + + var buildExists = m_TestListBuilds.Find(b => b.BuildName == req.BuildName) != null; + if (buildExists) throw new ApiException(); + + var osFamily = req.OsFamily switch + { + CreateBuildRequest.OsFamilyEnum.LINUX => CreateBuild200Response.OsFamilyEnum.LINUX, + _ => throw new ApiException() + }; + + var build = req.BuildType switch + { + CreateBuildRequest.BuildTypeEnum.CONTAINER => new CreateBuild200Response( + 1, req.BuildName, CreateBuild200Response.BuildTypeEnum.CONTAINER, + cfv: 5, osFamily: osFamily, updated: DateTime.Now, container: new ContainerImage("v1")), + CreateBuildRequest.BuildTypeEnum.FILEUPLOAD => new CreateBuild200Response( + 1, req.BuildName, CreateBuild200Response.BuildTypeEnum.FILEUPLOAD, + cfv: 5, osFamily: osFamily, updated: DateTime.Now, + ccd: new CCDDetails(new Guid(ValidBucketId), new Guid(ValidReleaseId))), + CreateBuildRequest.BuildTypeEnum.S3 => new CreateBuild200Response( + 1, req.BuildName, CreateBuild200Response.BuildTypeEnum.FILEUPLOAD, + cfv: 5, osFamily: osFamily, updated: DateTime.Now, + ccd: new CCDDetails(new Guid(ValidBucketId), new Guid(ValidReleaseId))), + _ => throw new ApiException() + }; + + return Task.FromResult(build); + }); + + DefaultBuildsClient.Setup(a => + a.ListBuildsAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // limit + It.IsAny(), // lastVal + It.IsAny(), // lastId + It.IsAny(), // sortBy + It.IsAny(), // sortDir + It.IsAny(), // partialFilter + 0, + CancellationToken.None + )).Returns(( + Guid projectId, + Guid environmentId, + string _, + Guid? _, + Guid? _, + string _, + string _, + string _, + int _, + CancellationToken _ + ) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + return Task.FromResult(m_TestListBuilds); + }); + + DefaultBuildsClient.Setup(a => + a.GetBuildAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // buildId + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, long buildId, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var build = m_TestBuilds.Find(b => b.BuildID == buildId); + if (build == null) throw new ApiException(); + + return Task.FromResult(build); + }); + DefaultBuildsClient.Setup(a => + a.DeleteBuildAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // buildId + null, // Dry Run + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, long buildId, bool _, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var build = GetBuildById(buildId); + if (build is null) throw new HttpRequestException(); + + return Task.CompletedTask; + }); + DefaultBuildsClient.Setup(a => + a.UpdateBuildAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // buildId + It.IsAny(), // update build request + 0, + CancellationToken.None + )).Returns(( + Guid projectId, + Guid environmentId, + long buildId, + UpdateBuildRequest _, + int _, + CancellationToken _ + ) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var build = m_TestBuilds.Find(b => b.BuildID == buildId); + if (build == null) throw new ApiException(); + + return Task.FromResult(build); + }); + + DefaultBuildsClient.Setup( + a => + a.CreateNewBuildVersionAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // buildId + It.IsAny(), // buildVersion + 0, + CancellationToken.None + )) + .Returns( + ( + Guid projectId, + Guid environmentId, + long buildId, + CreateNewBuildVersionRequest _, + int _, + CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var build = m_TestBuilds.Find(b => b.BuildID == buildId); + if (build == null) throw new ApiException(); + + return Task.FromResult(new object()); + }); + + DefaultBuildsClient.Setup( + a => a.GetBuildFilesAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // BuildId + It.IsAny(), + It.IsAny(), + 0, + CancellationToken.None + )) + .Returns( + (Guid projectId, Guid environmentId, long buildId, int limit, int offset, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var buildFilesRequestKey = BuildFilesRequestMockKey(buildId.ToString(), limit, offset); + + if (!m_FilesByBuildId.ContainsKey(buildFilesRequestKey)) throw new ApiException(); + + return Task.FromResult(m_FilesByBuildId[buildFilesRequestKey]); + }); + + + DefaultBuildsClient.Setup( + a => a.CreateOrUpdateBuildFileAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // Bui + It.IsAny(), + 0, + CancellationToken.None + )) + .Returns( + ( + Guid projectId, + Guid environmentId, + long buildId, + CreateOrUpdateBuildFileRequest request, + int _, + CancellationToken _) => + { + ValidateProjectEnvironmentThrowsException(projectId, environmentId); + ValidateIsBuildWithFilesThrowsException(buildId); + + var fileResponse = new BuildFilesListResultsInner( + "hashvalue", + "path", + "http://www.fakesite.io/signedUrl/" + request.Path); + + if (request.Path.Contains("uploaded")) + { + fileResponse.Uploaded = true; + return Task.FromResult(fileResponse); + } + + if (request.Path.Contains("fails")) throw new ApiException(); + + return Task.FromResult(fileResponse); + } + ); + + + DefaultBuildsClient.Setup( + a => a.DeleteBuildFileByPathAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // Bui + It.IsAny(), + 0, + CancellationToken.None + )) + .Returns( + (Guid projectId, Guid environmentId, long buildId, string filepath, int _, CancellationToken _) => + { + ValidateProjectEnvironmentThrowsException(projectId, environmentId); + ValidateIsBuildWithFilesThrowsException(buildId); + + if (!m_DeletableFiles.Contains(filepath)) + throw new ApiException(400, $"filepath: {filepath} is not in the allowed deletable files list"); + + + return Task.FromResult(new object()); + } + ); + + DefaultBuildsClient.Setup(a => + a.GetBuildInstallsAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // BuildId + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, long buildId, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var buildExists = m_TestListBuilds.Find(b => b.BuildID == buildId) != null; + if (!buildExists) throw new HttpRequestException(); + + return Task.FromResult(m_TestBuildInstalls); + }); + } + + bool ValidateProjectEnvironment(Guid projectId, Guid environmentId) + { + if (ValidProjects != null && !ValidProjects.Contains(projectId)) return false; + if (ValidEnvironments != null && !ValidEnvironments.Contains(environmentId)) return false; + return true; + } + + void ValidateProjectEnvironmentThrowsException(Guid projectId, Guid environmentId) + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + } + + void ValidateIsBuildWithFilesThrowsException(long buildId) + { + if (!m_BuildIdsThatCanBeUpdatedCreatedOrDeleted.Contains(buildId.ToString())) + throw new ApiException(404, $"buildId: {buildId} not found in mock data"); + } + + CreateBuild200Response? GetBuildById(long id) + { + return m_TestBuilds.FirstOrDefault(b => b.BuildID.Equals(id)); + } + + static string BuildFilesRequestMockKey(string buildId, int limit, int offset) + { + return $"{buildId}:{limit}:{offset}"; + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingBuildConfigurationsApiV1Mock.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingBuildConfigurationsApiV1Mock.cs new file mode 100644 index 0000000..0a093a6 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingBuildConfigurationsApiV1Mock.cs @@ -0,0 +1,165 @@ +using Moq; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Client; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Mocks; + +class GameServerHostingBuildConfigurationsApiV1Mock +{ + readonly List m_BuildConfigurationListItems = new() + { + new BuildConfigurationListItem( + buildName: ValidBuildName, + buildID: 1, + fleetID: new Guid(ValidFleetId), + fleetName: ValidFleetName, + id: ValidBuildConfigurationId, + name: ValidBuildConfigurationName, + updatedAt: new DateTime(2022, 10, 11), + createdAt: new DateTime(2022, 10, 11) + ) + }; + + readonly BuildConfiguration m_BuildConfiguration = new BuildConfiguration( + binaryPath: "/path/to/simple-go-server", + buildID: long.Parse(ValidBuildId), + buildName: ValidBuildName, + commandLine: "simple-go-server", + configuration: new List() + { + new ConfigEntry( 0, "key", "value"), + }, + cores: 2L, + createdAt: new DateTime(2022, 10, 11), + fleetID: new Guid(ValidFleetId), + fleetName: ValidFleetName, + id: ValidBuildConfigurationId, + memory: 800L, + name: ValidBuildConfigurationName, + queryType: "sqp", + speed: 1200L, + updatedAt:new DateTime(2022, 10, 11), + version: 1L + ); + + public Mock DefaultBuildConfigurationsClient = new(); + + public List? ValidEnvironments; + + public List? ValidProjects; + + public void SetUp() + { + DefaultBuildConfigurationsClient = new Mock(); + DefaultBuildConfigurationsClient.Setup(a => a.Configuration) + .Returns(new Configuration()); + + DefaultBuildConfigurationsClient.Setup(a => + a.GetBuildConfigurationAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // buildConfigurationId + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, long buildConfigurationId, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + return Task.FromResult(m_BuildConfiguration); + }); + + DefaultBuildConfigurationsClient.Setup(a => + a.ListBuildConfigurationsAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // fleetId + It.IsAny(), + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, Guid _, string? partialFilter, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + var results = m_BuildConfigurationListItems.AsEnumerable(); + if (partialFilter != null) + { + results = results.Where( + a => + { + var id = a.Id.ToString().Contains(partialFilter); + var name = a.Name.Contains(partialFilter); + return id || name; + } + ); + } + return Task.FromResult(results.ToList()); + }); + + DefaultBuildConfigurationsClient.Setup( + a => + a.CreateBuildConfigurationAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, BuildConfigurationCreateRequest createReq, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var buildConfig = new BuildConfiguration( + binaryPath: ValidBuildConfigurationBinaryPath, + buildID: long.Parse(ValidBuildId), + buildName: ValidBuildName, + commandLine: ValidBuildConfigurationCommandLine, + configuration: new List(), + name: ValidBuildConfigurationName, + queryType: ValidBuildConfigurationQueryType + ); + return Task.FromResult(buildConfig); + }); + + DefaultBuildConfigurationsClient.Setup(a => + a.DeleteBuildConfigurationAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // buildConfigurationId + null, + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, long buildConfigurationId, bool _, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + return Task.CompletedTask; + }); + + DefaultBuildConfigurationsClient.Setup( + a => + a.UpdateBuildConfigurationAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // buildConfigurationId + It.IsAny(), + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, long buildId, BuildConfigurationUpdateRequest _, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + return Task.FromResult(m_BuildConfiguration); + }); + } + + bool ValidateProjectEnvironment(Guid projectId, Guid environmentId) + { + if (ValidProjects != null && !ValidProjects.Contains(projectId)) return false; + if (ValidEnvironments != null && !ValidEnvironments.Contains(environmentId)) return false; + return true; + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingFleetsApiV1Mock.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingFleetsApiV1Mock.cs new file mode 100644 index 0000000..f374471 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingFleetsApiV1Mock.cs @@ -0,0 +1,372 @@ +using Moq; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Client; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Mocks; + +class GameServerHostingFleetsApiV1Mock +{ + static readonly List k_TestFleetItems = new() + { + new FleetListItem( + FleetListItem.AllocationTypeEnum.ALLOCATION, + new List(), + regions: new List(), + id: new Guid(ValidFleetId), + name: ValidFleetName, + osName: OsNameLinux, + servers: new Servers(new FleetServerBreakdown(new ServerStatus()), + new FleetServerBreakdown(new ServerStatus()), new FleetServerBreakdown(new ServerStatus())), + status: FleetListItem.StatusEnum.ONLINE + ), + new FleetListItem( + FleetListItem.AllocationTypeEnum.ALLOCATION, + new List(), + regions: new List(), + id: new Guid(ValidFleetId2), + name: ValidFleetName2, + osName: OsNameLinux, + servers: new Servers(new FleetServerBreakdown(new ServerStatus()), + new FleetServerBreakdown(new ServerStatus()), new FleetServerBreakdown(new ServerStatus())), + status: FleetListItem.StatusEnum.ONLINE + ) + }; + + static readonly List k_TestFleetRegions = new() + { + new FleetRegion1( + deleteTTL: 120, + disabledDeleteTTL: 60, + id: new Guid(ValidFleetId), + maxServers: 3, + minAvailableServers: 3, + regionID: new Guid(ValidRegionId), + regionName: ValidTemplateRegionName, + scalingEnabled: false, + shutdownTTL: 180 + ), + new FleetRegion1( + deleteTTL: 120, + disabledDeleteTTL: 60, + id: new Guid(ValidFleetId2), + maxServers: 3, + minAvailableServers: 3, + regionID: new Guid(ValidRegionId2), + regionName: ValidTemplateRegionName2, + scalingEnabled: false, + shutdownTTL: 180 + ) + }; + + static readonly List k_TestFleets = new() + { + new Fleet( + buildConfigurations: new List() + { + new(id: 1, name: "build config 1", buildName: "build 1", buildID: 1) + }, + fleetRegions: k_TestFleetRegions, + id: new Guid(ValidFleetId), + name: ValidFleetName, + osFamily: Fleet.OsFamilyEnum.LINUX, + osName: OsNameLinux, + servers: new Servers(new FleetServerBreakdown(new ServerStatus()), + new FleetServerBreakdown(new ServerStatus()), new FleetServerBreakdown(new ServerStatus())), + status: Fleet.StatusEnum.ONLINE, + allocationTTL: 10, + deleteTTL: 20, + disabledDeleteTTL: 25, + shutdownTTL: 30 + ), + new Fleet( + buildConfigurations: new List(), + fleetRegions: new List(), + id: new Guid(ValidFleetId2), + name: ValidFleetName2, + osFamily: Fleet.OsFamilyEnum.LINUX, + osName: OsNameLinux, + servers: new Servers(new FleetServerBreakdown(new ServerStatus()), + new FleetServerBreakdown(new ServerStatus()), new FleetServerBreakdown(new ServerStatus())), + status: Fleet.StatusEnum.ONLINE, + allocationTTL: 1, + deleteTTL: 2, + disabledDeleteTTL: 3, + shutdownTTL: 4 + ) + }; + + static readonly List k_TestFleetTemplateRegions = new() + { + new FleetRegionsTemplateListItem( + name: ValidTemplateRegionName, + regionID: new Guid(ValidTemplateRegionId) + ), + new FleetRegionsTemplateListItem( + name: ValidTemplateRegionName2, + regionID: new Guid(ValidTemplateRegionId2) + ) + }; + + static readonly List k_TestAvailableRegions = new() + { + new FleetRegionsTemplateListItem( + name: ValidTemplateRegionName, + regionID: new Guid(ValidTemplateRegionId) + ), + new FleetRegionsTemplateListItem( + name: ValidTemplateRegionName2, + regionID: new Guid(ValidTemplateRegionId2) + ) + }; + + static readonly NewFleetRegion k_TestNewFleetRegion = new( + new Guid(ValidTemplateRegionId), + 1, + 2, + new Guid(ValidTemplateRegionId), + regionName: ValidTemplateRegionName + ); + + static readonly UpdatedFleetRegion k_UpdatedFleetRegion = new( + deleteTTL: 120, + disabledDeleteTTL: 60, + id: Guid.Parse(ValidFleetId), + maxServers: 3, + minAvailableServers: 3, + regionID: Guid.Parse(ValidRegionId), + regionName: ValidTemplateRegionName, + scalingEnabled: false, + shutdownTTL: 180 + ); + + public Mock DefaultFleetsClient = new(); + + public List? ValidEnvironments; + + public List? ValidProjects; + + public void SetUp() + { + DefaultFleetsClient = new Mock(); + DefaultFleetsClient.Setup(a => a.Configuration) + .Returns(new Configuration()); + + DefaultFleetsClient.Setup(a => + a.GetFleetAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // fleetId + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, Guid fleetId, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var fleet = k_TestFleets.Find(b => b.Id == fleetId); + if (fleet == null) throw new HttpRequestException(); + + return Task.FromResult(fleet); + }); + + DefaultFleetsClient.Setup(a => a.ListFleetsAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + return Task.FromResult(k_TestFleetItems); + }); + + DefaultFleetsClient.Setup(a => + a.CreateFleetAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, FleetCreateRequest createReq, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var fleet = new Fleet( + buildConfigurations: new List(), + fleetRegions: new List(), + id: new Guid(ValidFleetId), + name: createReq.Name, + osFamily: (Fleet.OsFamilyEnum)createReq.OsFamily!, + osName: OsNameLinux, + servers: new Servers(new FleetServerBreakdown(new ServerStatus()), + new FleetServerBreakdown(new ServerStatus()), new FleetServerBreakdown(new ServerStatus())), + status: Fleet.StatusEnum.ONLINE, + allocationTTL: 10, + deleteTTL: 20, + disabledDeleteTTL: 25, + shutdownTTL: 30 + ); + + return Task.FromResult(fleet); + }); + + DefaultFleetsClient.Setup(a => + a.CreateFleetAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, FleetCreateRequest createReq, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var fleet = new Fleet( + buildConfigurations: new List(), + fleetRegions: new List(), + id: new Guid(ValidFleetId), + name: createReq.Name, + osFamily: (Fleet.OsFamilyEnum)createReq.OsFamily!, + osName: OsNameLinux, + servers: new Servers(new FleetServerBreakdown(new ServerStatus()), + new FleetServerBreakdown(new ServerStatus()), new FleetServerBreakdown(new ServerStatus())), + status: Fleet.StatusEnum.ONLINE, + allocationTTL: 10, + deleteTTL: 20, + disabledDeleteTTL: 25, + shutdownTTL: 30 + ); + + return Task.FromResult(fleet); + }); + + DefaultFleetsClient.Setup(a => a.DeleteFleetAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // fleetId + null, // Dry Run + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, Guid fleetId, bool _, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var fleet = GetFleetById(fleetId); + if (fleet is null) throw new HttpRequestException(); + + return Task.CompletedTask; + }); + + DefaultFleetsClient.Setup(a => a.UpdateFleetAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // fleetId + It.IsAny(), // update fleet request + 0, + CancellationToken.None + )).Returns(( + Guid projectId, + Guid environmentId, + Guid fleetId, + FleetUpdateRequest _, + int _, + CancellationToken _ + ) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var fleet = GetFleetById(fleetId); + if (fleet == null) throw new ApiException(); + + return Task.FromResult(fleet); + }); + + DefaultFleetsClient.Setup(a => a.ListTemplateFleetRegionsAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + return Task.FromResult(k_TestFleetTemplateRegions); + }); + + DefaultFleetsClient.Setup(a => a.GetAvailableFleetRegionsAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // fleetId + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, Guid fleetId, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var fleet = k_TestFleets.Find(b => b.Id == fleetId); + if (fleet == null) throw new HttpRequestException(); + + return Task.FromResult(k_TestAvailableRegions); + }); + + DefaultFleetsClient.Setup(a => a.AddFleetRegionAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // fleetId + It.IsAny(), + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, Guid fleetId, AddRegionRequest _, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var fleet = k_TestFleets.Find(b => b.Id == fleetId); + if (fleet == null) throw new HttpRequestException(); + + return Task.FromResult(k_TestNewFleetRegion); + }); + + DefaultFleetsClient.Setup(a => a.UpdateFleetRegionAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // fleetId + It.IsAny(), // regionId + It.IsAny(), + 0, + CancellationToken.None + )).Returns((Guid projectId, Guid environmentId, Guid fleetId, Guid regionId, UpdateRegionRequest _, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var fleet = k_TestFleets.Find(b => b.Id == fleetId); + if (fleet == null) throw new HttpRequestException(); + + var region = fleet.FleetRegions.Find(r => r.RegionID == regionId); + if (region == null) throw new HttpRequestException(); + + return Task.FromResult(k_UpdatedFleetRegion); + } + ); + } + + bool ValidateProjectEnvironment(Guid projectId, Guid environmentId) + { + if (ValidProjects != null && !ValidProjects.Contains(projectId)) return false; + if (ValidEnvironments != null && !ValidEnvironments.Contains(environmentId)) return false; + return true; + } + + static Fleet? GetFleetById(Guid id) + { + return k_TestFleets.FirstOrDefault(f => f.Id.Equals(id)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingServersApiV1Mock.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingServersApiV1Mock.cs new file mode 100644 index 0000000..42279e0 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Mocks/GameServerHostingServersApiV1Mock.cs @@ -0,0 +1,148 @@ +using Moq; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Client; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Mocks; + +class GameServerHostingServersApiV1Mock +{ + readonly List m_TestServers = new() + { + new Server( + id: ValidServerId, + ip: "0.0.0.0", + port: 9000, + machineID: ValidMachineId, + locationID: ValidLocationId, + locationName: ValidLocationName, + machineName:"", + machineSpec: new MachineSpec(""), + hardwareType: Server.HardwareTypeEnum.CLOUD, + fleetID: new Guid(ValidFleetId), + fleetName: ValidFleetName, + buildConfigurationID: ValidBuildConfigurationId, + buildConfigurationName: ValidBuildConfigurationName, + buildName: ValidBuildName, + deleted: false + ) + }; + + public Mock DefaultServersClient = new(); + + public List? ValidEnvironments; + + public List? ValidProjects; + + public void SetUp() + { + DefaultServersClient = new Mock(); + DefaultServersClient.Setup(a => a.Configuration) + .Returns(new Configuration()); + + DefaultServersClient.Setup( + a => a.GetServerAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // serverID + 0, + CancellationToken.None + )) + .Returns( + (Guid projectId, Guid environmentId, long serverId, int _, CancellationToken _) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var server = m_TestServers.Find(b => b.Id == serverId); + if (server == null) throw new HttpRequestException(); + + return Task.FromResult(server); + }); + + DefaultServersClient.Setup( + a => a.ListServersAsync( + It.IsAny(), // projectId + It.IsAny(), // environmentId + It.IsAny(), // limit + It.IsAny(), // lastId + It.IsAny(), // lastValue + It.IsAny(), // sortBy + It.IsAny(), // sortDirection + It.IsAny(), // fleetId + It.IsAny(), // locationId + It.IsAny(), // buildConfigurationId + It.IsAny(), // hardwareType + It.IsAny(), // partial + It.IsAny(), // status + 0, + CancellationToken.None + )) + .Returns( + ( + Guid projectId, + Guid environmentId, + string? _, + Guid? _, + string? _, + string? _, + string? _, + Guid? fleetId, + string? _, + string? buildConfigurationId, + string? _, + string? partial, + string? status, + int _, + CancellationToken _ + ) => + { + var validated = ValidateProjectEnvironment(projectId, environmentId); + if (!validated) throw new HttpRequestException(); + + var results = m_TestServers.AsEnumerable(); + + if (fleetId != null) + { + results = results.Where(a => a.FleetID == fleetId); + } + + if (buildConfigurationId != null) + { + var isLong = long.TryParse(buildConfigurationId, out var buildConfigurationIdString); + if (!isLong) throw new HttpRequestException(); + results = results.Where(a => a.BuildConfigurationID == buildConfigurationIdString); + } + + if (partial != null) + { + results = results.Where( + a => + { + var id = a.Id.ToString().Contains(partial); + var ip = a.Ip.Contains(partial); + var machine = a.MachineID.ToString().Contains(partial); + return id || ip || machine; + } + ); + } + + if (status != null) + { + var validStatus = Enum.TryParse(status, out Server.StatusEnum statusEnum); + if (!validStatus) throw new HttpRequestException(); + results = results.Where(a => a.Status == statusEnum); + } + + return Task.FromResult(results.ToList()); + } + ); + } + + bool ValidateProjectEnvironment(Guid projectId, Guid environmentId) + { + if (ValidProjects != null && !ValidProjects.Contains(projectId)) return false; + if (ValidEnvironments != null && !ValidEnvironments.Contains(environmentId)) return false; + return true; + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildConfigurationOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildConfigurationOutputTests.cs new file mode 100644 index 0000000..fb49a07 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildConfigurationOutputTests.cs @@ -0,0 +1,74 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using Unity.Services.Cli.TestUtils; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +class BuildConfigurationOutputTests +{ + [SetUp] + public void SetUp() + { + m_BuildConfiguration = new BuildConfiguration( + binaryPath: "/path/to/simple-go-server", + buildID: long.Parse(ValidBuildId), + buildName: ValidBuildName, + commandLine: "simple-go-server", + configuration: new List() + { + new ConfigEntry( + id: 0, + key: "key", + value: "value" + ), + }, + cores: 2L, + createdAt: new DateTime(2022, 10, 11), + fleetID: new Guid(ValidFleetId), + fleetName: ValidFleetName, + id: ValidBuildConfigurationId, + memory: 800L, + name: ValidBuildConfigurationName, + queryType: "sqp", + speed: 1200L, + updatedAt: new DateTime(2022, 10, 11), + version: 1L + ); + } + + BuildConfiguration? m_BuildConfiguration; + + [Test] + public void ConstructBuildConfigurationOutputWithValidBuildConfiguration() + { + BuildConfigurationOutput output = new(m_BuildConfiguration!); + Assert.Multiple(() => + { + Assert.That(output.BuildId, Is.EqualTo(m_BuildConfiguration!.BuildID)); + Assert.That(output.BuildName, Is.EqualTo(m_BuildConfiguration!.BuildName)); + Assert.That(output.CommandLine, Is.EqualTo(m_BuildConfiguration!.CommandLine)); + Assert.That(output.Cores, Is.EqualTo(m_BuildConfiguration!.Cores)); + Assert.That(output.CreatedAt, Is.EqualTo(m_BuildConfiguration!.CreatedAt)); + Assert.That(output.FleetId, Is.EqualTo(m_BuildConfiguration!.FleetID)); + Assert.That(output.FleetName, Is.EqualTo(m_BuildConfiguration!.FleetName)); + Assert.That(output.Id, Is.EqualTo(m_BuildConfiguration!.Id)); + Assert.That(output.Memory, Is.EqualTo(m_BuildConfiguration!.Memory)); + Assert.That(output.Name, Is.EqualTo(m_BuildConfiguration!.Name)); + Assert.That(output.QueryType, Is.EqualTo(m_BuildConfiguration!.QueryType)); + Assert.That(output.Speed, Is.EqualTo(m_BuildConfiguration!.Speed)); + Assert.That(output.UpdatedAt, Is.EqualTo(m_BuildConfiguration!.UpdatedAt)); + Assert.That(output.Version, Is.EqualTo(m_BuildConfiguration!._Version)); + + for (var i = 0; i < m_BuildConfiguration!._Configuration.Count; i++) + { + Assert.That(output.Configuration[i].Id, Is.EqualTo(m_BuildConfiguration!._Configuration[i].Id)); + Assert.That(output.Configuration[i].Key, Is.EqualTo(m_BuildConfiguration!._Configuration[i].Key)); + Assert.That(output.Configuration[i].Value, Is.EqualTo(m_BuildConfiguration!._Configuration[i].Value)); + } + }); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemFailuresItemOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemFailuresItemOutputTests.cs new file mode 100644 index 0000000..f9824fb --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemFailuresItemOutputTests.cs @@ -0,0 +1,44 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +class BuildInstallsItemFailuresItemOutputTests +{ + [SetUp] + public void Setup() + { + m_Failure = new BuildListInner1FailuresInner( + 123, + "reason", + DateTime.Now + ); + } + + BuildListInner1FailuresInner? m_Failure; + + [Test] + public void ConstructBuildInstallsItemFailuresItemOutputWithValidFailure() + { + BuildInstallsItemFailuresItemOutput output = new(m_Failure!); + Assert.Multiple(() => + { + Assert.That(output.MachineId, Is.EqualTo(m_Failure?.MachineID)); + Assert.That(output.Reason, Is.EqualTo(m_Failure?.Reason)); + Assert.That(output.Updated, Is.EqualTo(m_Failure?.Updated)); + }); + } + + [Test] + public void BuildInstallsItemFailuresItemOutputToString() + { + var sb = new StringBuilder(); + BuildInstallsItemFailuresItemOutput output = new(m_Failure!); + sb.AppendLine($"machineId: {output.MachineId}"); + sb.AppendLine($"reason: {output.Reason}"); + sb.AppendLine($"updated: {output.Updated}"); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemFailuresOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemFailuresOutputTests.cs new file mode 100644 index 0000000..3238440 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemFailuresOutputTests.cs @@ -0,0 +1,58 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +class BuildInstallsItemFailuresOutputTests +{ + [SetUp] + public void Setup() + { + m_Failures = new List + { + new( + 123, + "reason", + DateTime.Now + ), + new( + 456, + "reason2", + DateTime.Now + ) + }; + } + + List? m_Failures; + + [Test] + public void ConstructBuildInstallsItemFailuresOutputWithValidFailures() + { + BuildInstallsItemFailuresOutput output = new(m_Failures!); + Assert.That(output, Has.Count.EqualTo(m_Failures!.Count)); + for (var i = 0; i < output.Count; i++) + Assert.Multiple(() => + { + Assert.That(output[i].MachineId, Is.EqualTo(m_Failures[i].MachineID)); + Assert.That(output[i].Reason, Is.EqualTo(m_Failures[i].Reason)); + Assert.That(output[i].Updated, Is.EqualTo(m_Failures[i].Updated)); + }); + } + + [Test] + public void BuildInstallsItemFailuresOutputToString() + { + var sb = new StringBuilder(); + BuildInstallsItemFailuresOutput output = new(m_Failures!); + foreach (var failure in output) + { + sb.AppendLine($"- machineId: {failure.MachineId}"); + sb.AppendLine($" reason: {failure.Reason}"); + sb.AppendLine($" updated: {failure.Updated}"); + } + + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemOutputTests.cs new file mode 100644 index 0000000..015eb63 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemOutputTests.cs @@ -0,0 +1,100 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +class BuildInstallsItemOutputTests +{ + [SetUp] + public void SetUp() + { + m_Install = new BuildListInner1( + new CCDDetails( + Guid.Parse(ValidBucketId), + Guid.Parse(ValidReleaseId) + ), + completedMachines: 1, + container: new ContainerImage( + "tag" + ), + failures: new List + { + new( + 1234, + "failure", + DateTime.Now + ) + }, + fleetName: "fleet name", + pendingMachines: 1, + regions: new List + { + new( + 1, + 1, + 1, + "region name" + ) + } + ); + } + + BuildListInner1? m_Install; + + [Test] + public void ConstructBuildInstallsItemOutputWithValidInstalls() + { + BuildInstallsItemOutput output = new(m_Install!); + Assert.Multiple(() => + { + Assert.That(output.FleetName, Is.EqualTo(m_Install!.FleetName)); + Assert.That(output.Ccd?.BucketId, Is.EqualTo(m_Install!.Ccd.BucketID)); + Assert.That(output.Ccd?.ReleaseId, Is.EqualTo(m_Install!.Ccd.ReleaseID)); + Assert.That(output.Container, Is.EqualTo(m_Install!.Container)); + Assert.That(output.PendingMachines, Is.EqualTo(m_Install!.PendingMachines)); + Assert.That(output.CompletedMachines, Is.EqualTo(m_Install!.CompletedMachines)); + + for (var i = 0; i < m_Install!.Failures.Count; i++) + { + Assert.That(output.Failures[i].MachineId, Is.EqualTo(m_Install!.Failures[i].MachineID)); + Assert.That(output.Failures[i].Reason, Is.EqualTo(m_Install!.Failures[i].Reason)); + Assert.That(output.Failures[i].Updated, Is.EqualTo(m_Install!.Failures[i].Updated)); + } + + for (var i = 0; i < m_Install!.Regions.Count; i++) + { + Assert.That(output.Regions[i].RegionName, Is.EqualTo(m_Install!.Regions[i].RegionName)); + Assert.That(output.Regions[i].PendingMachines, Is.EqualTo(m_Install!.Regions[i].PendingMachines)); + Assert.That(output.Regions[i].CompletedMachines, Is.EqualTo(m_Install!.Regions[i].CompletedMachines)); + Assert.That(output.Regions[i].Failures, Is.EqualTo(m_Install!.Regions[i].Failures)); + } + }); + } + + [Test] + public void BuildInstallsItemOutputToString() + { + BuildInstallsItemOutput output = new(m_Install!); + var sb = new StringBuilder(); + sb.AppendLine("fleetName: fleet name"); + sb.AppendLine("ccd:"); + sb.AppendLine(" bucketId: " + ValidBucketId); + sb.AppendLine(" releaseId: " + ValidReleaseId); + sb.AppendLine("container:"); + sb.AppendLine(" imageTag: tag"); + sb.AppendLine("pendingMachines: 1"); + sb.AppendLine("completedMachines: 1"); + sb.AppendLine("failures:"); + sb.AppendLine("- machineId: 1234"); + sb.AppendLine(" reason: failure"); + sb.AppendLine(" updated: " + m_Install!.Failures[0].Updated); + sb.AppendLine("regions:"); + sb.AppendLine("- regionName: region name"); + sb.AppendLine(" pendingMachines: 1"); + sb.AppendLine(" completedMachines: 1"); + sb.AppendLine(" failures: 1"); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemRegionsItemOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemRegionsItemOutputTests.cs new file mode 100644 index 0000000..9103d71 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemRegionsItemOutputTests.cs @@ -0,0 +1,47 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +class BuildInstallsItemRegionsItemOutputTests +{ + [SetUp] + public void Setup() + { + m_Region = new RegionsInner( + 1, + 2, + 3, + "regionName" + ); + } + + RegionsInner? m_Region; + + [Test] + public void ConstructBuildInstallsItemRegionsItemOutputWithValidRegion() + { + BuildInstallsItemRegionsItemOutput output = new(m_Region!); + Assert.Multiple(() => + { + Assert.That(output.RegionName, Is.EqualTo(m_Region?.RegionName)); + Assert.That(output.PendingMachines, Is.EqualTo(m_Region?.PendingMachines)); + Assert.That(output.CompletedMachines, Is.EqualTo(m_Region?.CompletedMachines)); + Assert.That(output.Failures, Is.EqualTo(m_Region?.Failures)); + }); + } + + [Test] + public void BuildInstallsItemRegionsItemOutputToString() + { + var sb = new StringBuilder(); + BuildInstallsItemRegionsItemOutput output = new(m_Region!); + sb.AppendLine($"regionName: {output.RegionName}"); + sb.AppendLine($"pendingMachines: {output.PendingMachines}"); + sb.AppendLine($"completedMachines: {output.CompletedMachines}"); + sb.AppendLine($"failures: {output.Failures}"); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemRegionsOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemRegionsOutputTests.cs new file mode 100644 index 0000000..9bd7319 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsItemRegionsOutputTests.cs @@ -0,0 +1,56 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +class BuildInstallsItemRegionsOutputTests +{ + [SetUp] + public void Setup() + { + m_Regions = new List + { + new( + 1, + 2, + 3, + "regionName" + ) + }; + } + + List? m_Regions; + + [Test] + public void ConstructBuildInstallsItemRegionsOutputWithValidRegions() + { + BuildInstallsItemRegionsOutput output = new(m_Regions!); + Assert.That(output, Has.Count.EqualTo(m_Regions!.Count)); + for (var i = 0; i < output.Count; i++) + Assert.Multiple(() => + { + Assert.That(output[i].RegionName, Is.EqualTo(m_Regions[i].RegionName)); + Assert.That(output[i].PendingMachines, Is.EqualTo(m_Regions[i].PendingMachines)); + Assert.That(output[i].CompletedMachines, Is.EqualTo(m_Regions[i].CompletedMachines)); + Assert.That(output[i].Failures, Is.EqualTo(m_Regions[i].Failures)); + }); + } + + [Test] + public void BuildInstallsItemRegionsOutputToString() + { + var sb = new StringBuilder(); + BuildInstallsItemRegionsOutput output = new(m_Regions!); + foreach (var region in output) + { + sb.AppendLine($"- regionName: {region.RegionName}"); + sb.AppendLine($" pendingMachines: {region.PendingMachines}"); + sb.AppendLine($" completedMachines: {region.CompletedMachines}"); + sb.AppendLine($" failures: {region.Failures}"); + } + + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsOutputTests.cs new file mode 100644 index 0000000..300b452 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildInstallsOutputTests.cs @@ -0,0 +1,160 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +class BuildInstallsOutputTests +{ + [SetUp] + public void SetUp() + { + m_Installs = new List(); + + m_Installs.Add(new BuildListInner1( + new CCDDetails( + Guid.Parse(ValidBucketId), + Guid.Parse(ValidReleaseId) + ), + completedMachines: 1, + container: new ContainerImage( + "tag" + ), + failures: new List + { + new( + 1234, + "failure", + DateTime.Now + ) + }, + fleetName: "fleet name", + pendingMachines: 1, + regions: new List + { + new( + 1, + 1, + 1, + "region name" + ) + } + ) + ); + + m_Installs.Add(new BuildListInner1( + new CCDDetails( + Guid.Parse(ValidBucketId), + Guid.Parse(ValidReleaseId) + ), + completedMachines: 3, + container: new ContainerImage( + "tag" + ), + failures: new List + { + new( + 3456, + "failure", + DateTime.Now + ) + }, + fleetName: "another fleet name", + pendingMachines: 2, + regions: new List + { + new( + 3, + 1, + 2, + "another region name" + ) + } + ) + ); + } + + List? m_Installs; + + [Test] + public void ConstructBuildInstallsOutputWithValidInstalls() + { + BuildInstallsOutput output = new(m_Installs!); + Assert.That(output, Has.Count.EqualTo(m_Installs!.Count)); + for (var i = 0; i < output.Count; i++) + Assert.Multiple(() => + { + Assert.That(output[i].FleetName, Is.EqualTo(m_Installs[i].FleetName)); + Assert.That(output[i].Ccd?.BucketId, Is.EqualTo(m_Installs[i].Ccd.BucketID)); + Assert.That(output[i].Ccd?.ReleaseId, Is.EqualTo(m_Installs[i].Ccd.ReleaseID)); + Assert.That(output[i].Container, Is.EqualTo(m_Installs[i].Container)); + Assert.That(output[i].PendingMachines, Is.EqualTo(m_Installs[i].PendingMachines)); + Assert.That(output[i].CompletedMachines, Is.EqualTo(m_Installs[i].CompletedMachines)); + + for (var j = 0; j < output[i].Failures.Count; j++) + { + Assert.That(output[i].Failures[j].MachineId, + Is.EqualTo(m_Installs[i].Failures[j].MachineID)); + Assert.That(output[i].Failures[j].Reason, + Is.EqualTo(m_Installs[i].Failures[j].Reason)); + Assert.That(output[i].Failures[j].Updated, + Is.EqualTo(m_Installs[i].Failures[j].Updated)); + } + + for (var j = 0; j < output[i].Regions.Count; j++) + { + Assert.That(output[i].Regions[j].RegionName, + Is.EqualTo(m_Installs[i].Regions[j].RegionName)); + Assert.That(output[i].Regions[j].PendingMachines, + Is.EqualTo(m_Installs[i].Regions[j].PendingMachines)); + Assert.That(output[i].Regions[j].CompletedMachines, + Is.EqualTo(m_Installs[i].Regions[j].CompletedMachines)); + Assert.That(output[i].Regions[j].Failures, + Is.EqualTo(m_Installs[i].Regions[j].Failures)); + } + }); + } + + [Test] + public void BuildInstallsOutputToString() + { + BuildInstallsOutput output = new(m_Installs!); + var sb = new StringBuilder(); + sb.AppendLine("- fleetName: fleet name"); + sb.AppendLine(" ccd:"); + sb.AppendLine(" bucketId: " + ValidBucketId); + sb.AppendLine(" releaseId: " + ValidReleaseId); + sb.AppendLine(" container:"); + sb.AppendLine(" imageTag: tag"); + sb.AppendLine(" pendingMachines: 1"); + sb.AppendLine(" completedMachines: 1"); + sb.AppendLine(" failures:"); + sb.AppendLine(" - machineId: 1234"); + sb.AppendLine(" reason: failure"); + sb.AppendLine(" updated: " + m_Installs?[0].Failures[0].Updated); + sb.AppendLine(" regions:"); + sb.AppendLine(" - regionName: region name"); + sb.AppendLine(" pendingMachines: 1"); + sb.AppendLine(" completedMachines: 1"); + sb.AppendLine(" failures: 1"); + sb.AppendLine("- fleetName: another fleet name"); + sb.AppendLine(" ccd:"); + sb.AppendLine(" bucketId: " + ValidBucketId); + sb.AppendLine(" releaseId: " + ValidReleaseId); + sb.AppendLine(" container:"); + sb.AppendLine(" imageTag: tag"); + sb.AppendLine(" pendingMachines: 2"); + sb.AppendLine(" completedMachines: 3"); + sb.AppendLine(" failures:"); + sb.AppendLine(" - machineId: 3456"); + sb.AppendLine(" reason: failure"); + sb.AppendLine(" updated: " + m_Installs?[1].Failures[0].Updated); + sb.AppendLine(" regions:"); + sb.AppendLine(" - regionName: another region name"); + sb.AppendLine(" pendingMachines: 2"); + sb.AppendLine(" completedMachines: 3"); + sb.AppendLine(" failures: 1"); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildListOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildListOutputTests.cs new file mode 100644 index 0000000..b60b0f2 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildListOutputTests.cs @@ -0,0 +1,96 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +class BuildListOutputTests +{ + [SetUp] + public void SetUp() + { + m_Builds = new List + { + new( + buildID: 1, + buildName: "Test Build 1", + buildConfigurations: 1, + syncStatus: BuildListInner.SyncStatusEnum.SYNCED, + ccd: new CCDDetails( + Guid.Parse(ValidBucketId), + Guid.Parse(ValidReleaseId) + ), + osFamily: BuildListInner.OsFamilyEnum.LINUX, + updated: DateTime.Now + ), + new( + buildID: 2, + buildName: "Test Build 2", + buildConfigurations: 2, + syncStatus: BuildListInner.SyncStatusEnum.PENDING, + container: new ContainerImage("2"), + osFamily: BuildListInner.OsFamilyEnum.LINUX, + updated: DateTime.Now + ) + }; + } + + List? m_Builds; + + [Test] + public void ConstructBuildListOutputWithValidList() + { + BuildListOutput output = new(m_Builds!); + Assert.That(output, Has.Count.EqualTo(m_Builds!.Count)); + for (var i = 0; i < output.Count; i++) + Assert.Multiple(() => + { + Assert.That(output[i].BuildId, Is.EqualTo(m_Builds[i].BuildID)); + Assert.That(output[i].BuildName, Is.EqualTo(m_Builds[i].BuildName)); + Assert.That(output[i].BuildConfigurations, Is.EqualTo(m_Builds[i].BuildConfigurations)); + Assert.That(output[i].SyncStatus, Is.EqualTo(m_Builds[i].SyncStatus)); + // ReSharper disable ConditionalAccessQualifierIsNonNullableAccordingToAPIContract + Assert.That(output[i].Ccd?.BucketId, Is.EqualTo(m_Builds[i].Ccd?.BucketID)); + Assert.That(output[i].Ccd?.ReleaseId, Is.EqualTo(m_Builds[i].Ccd?.ReleaseID)); + // ReSharper restore ConditionalAccessQualifierIsNonNullableAccordingToAPIContract + Assert.That(output[i].Container, Is.EqualTo(m_Builds[i].Container)); + Assert.That(output[i].OsFamily, Is.EqualTo(m_Builds[i].OsFamily)); + Assert.That(output[i].Updated, Is.EqualTo(m_Builds[i].Updated)); + }); + } + + [Test] + public void BuildListOutputToString() + { + BuildListOutput output = new(m_Builds!); + var sb = new StringBuilder(); + foreach (var build in output) + { + sb.AppendLine($"- buildName: {build.BuildName}"); + sb.AppendLine($" buildId: {build.BuildId}"); + sb.AppendLine($" osFamily: {build.OsFamily}"); + sb.AppendLine($" updated: {build.Updated}"); + sb.AppendLine($" buildConfigurations: {build.BuildConfigurations}"); + sb.AppendLine($" syncStatus: {build.SyncStatus}"); + + if (build.Ccd != null) + { + sb.AppendLine(" ccd:"); + sb.AppendLine($" bucketId: {build.Ccd?.BucketId}"); + sb.AppendLine($" releaseId: {build.Ccd?.ReleaseId}"); + } + + // Need to ignore some warning here because the nullable API contract is not correct + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + // ReSharper disable once InvertIf + if (build.Container != default) + { + sb.AppendLine(" container:"); + sb.AppendLine($" imageTag: {build.Container.ImageTag}"); + } + } + + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildOutputTests.cs new file mode 100644 index 0000000..5b4f45a --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/BuildOutputTests.cs @@ -0,0 +1,99 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +class BuildOutputTests +{ + [SetUp] + public void SetUp() + { + m_BuildListItem = new BuildListInner( + buildID: 1, + buildName: "Test Build 1", + buildConfigurations: 1, + syncStatus: BuildListInner.SyncStatusEnum.SYNCED, + ccd: new CCDDetails( + Guid.Parse(ValidBucketId), + Guid.Parse(ValidReleaseId) + ), + osFamily: BuildListInner.OsFamilyEnum.LINUX, + updated: DateTime.Now + ); + m_BuildCreateResponse = new CreateBuild200Response( + 1, + "Test Build 1", + syncStatus: CreateBuild200Response.SyncStatusEnum.SYNCED, + ccd: new CCDDetails( + Guid.Parse(ValidBucketId), + Guid.Parse(ValidReleaseId) + ), + osFamily: CreateBuild200Response.OsFamilyEnum.LINUX, + updated: DateTime.Now + ); + } + + BuildListInner? m_BuildListItem; + CreateBuild200Response? m_BuildCreateResponse; + + [Test] + public void ConstructBuildOutputWithValidBuild() + { + BuildOutput output = new(m_BuildListItem!); + Assert.Multiple(() => + { + Assert.That(output.BuildId, Is.EqualTo(m_BuildListItem!.BuildID)); + Assert.That(output.BuildName, Is.EqualTo(m_BuildListItem!.BuildName)); + Assert.That(output.BuildConfigurations, Is.EqualTo(m_BuildListItem!.BuildConfigurations)); + Assert.That(output.SyncStatus, Is.EqualTo(m_BuildListItem!.SyncStatus)); + Assert.That(output.Ccd?.BucketId, Is.EqualTo(m_BuildListItem!.Ccd.BucketID)); + Assert.That(output.Ccd?.ReleaseId, Is.EqualTo(m_BuildListItem!.Ccd.ReleaseID)); + Assert.That(output.Container, Is.EqualTo(m_BuildListItem!.Container)); + Assert.That(output.OsFamily, Is.EqualTo(m_BuildListItem!.OsFamily)); + Assert.That(output.Updated, Is.EqualTo(m_BuildListItem!.Updated)); + }); + output = new BuildOutput(m_BuildCreateResponse!); + Assert.Multiple(() => + { + Assert.That(output.BuildId, Is.EqualTo(m_BuildCreateResponse!.BuildID)); + Assert.That(output.BuildName, Is.EqualTo(m_BuildCreateResponse!.BuildName)); + Assert.That(output.BuildConfigurations, Is.Null); + Assert.That(output.SyncStatus, Is.EqualTo((BuildListInner.SyncStatusEnum)m_BuildCreateResponse!.SyncStatus)); + Assert.That(output.Ccd?.BucketId, Is.EqualTo(m_BuildCreateResponse!.Ccd.BucketID)); + Assert.That(output.Ccd?.ReleaseId, Is.EqualTo(m_BuildCreateResponse!.Ccd.ReleaseID)); + Assert.That(output.Container, Is.EqualTo(m_BuildCreateResponse!.Container)); + Assert.That(output.OsFamily, Is.EqualTo((BuildListInner.OsFamilyEnum?)m_BuildCreateResponse!.OsFamily)); + Assert.That(output.Updated, Is.EqualTo(m_BuildCreateResponse!.Updated)); + }); + } + + [Test] + public void BuildOutputToString() + { + var sb = new StringBuilder(); + BuildOutput output = new(m_BuildListItem!); + sb.AppendLine("buildName: Test Build 1"); + sb.AppendLine("buildId: 1"); + sb.AppendLine("osFamily: LINUX"); + sb.AppendLine("updated: " + m_BuildListItem!.Updated); + sb.AppendLine("buildConfigurations: 1"); + sb.AppendLine("syncStatus: SYNCED"); + sb.AppendLine("ccd:"); + sb.AppendLine(" bucketId: " + ValidBucketId); + sb.AppendLine(" releaseId: " + ValidReleaseId); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + sb.Clear(); + output = new BuildOutput(m_BuildCreateResponse!); + sb.AppendLine("buildName: Test Build 1"); + sb.AppendLine("buildId: 1"); + sb.AppendLine("osFamily: LINUX"); + sb.AppendLine("updated: " + m_BuildCreateResponse!.Updated); + sb.AppendLine("syncStatus: SYNCED"); + sb.AppendLine("ccd:"); + sb.AppendLine(" bucketId: " + ValidBucketId); + sb.AppendLine(" releaseId: " + ValidReleaseId); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/CcdOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/CcdOutputTests.cs new file mode 100644 index 0000000..dde28b8 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/CcdOutputTests.cs @@ -0,0 +1,42 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +class CcdOutputTests +{ + [SetUp] + public void Setup() + { + m_CcdDetails = new CCDDetails( + Guid.Parse(ValidBucketId), + Guid.Parse(ValidReleaseId) + ); + } + + CCDDetails? m_CcdDetails; + + [Test] + public void ConstructCcdOutputWithValidCcdDetails() + { + CcdOutput output = new(m_CcdDetails); + Assert.Multiple(() => + { + Assert.That(output.BucketId, Is.EqualTo(m_CcdDetails?.BucketID)); + Assert.That(output.ReleaseId, Is.EqualTo(m_CcdDetails?.ReleaseID)); + }); + } + + + [Test] + public void CcdOutputToString() + { + var sb = new StringBuilder(); + CcdOutput output = new(m_CcdDetails); + sb.AppendLine($"bucketId: {output.BucketId}"); + sb.AppendLine($"releaseId: {output.ReleaseId}"); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetGetOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetGetOutputTests.cs new file mode 100644 index 0000000..531dbb4 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetGetOutputTests.cs @@ -0,0 +1,90 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +public class FleetGetOutputTests +{ + [SetUp] + public void SetUp() + { + m_Fleet = new Fleet( + buildConfigurations: new List(), + fleetRegions: new List(), + id: new Guid(ValidFleetId), + name: ValidFleetName, + osFamily: Fleet.OsFamilyEnum.LINUX, + osName: OsNameLinux, + servers: new Servers(new FleetServerBreakdown(new ServerStatus()), + new FleetServerBreakdown(new ServerStatus()), new FleetServerBreakdown(new ServerStatus())), + status: Fleet.StatusEnum.ONLINE, + allocationTTL: 10, + deleteTTL: 20, + disabledDeleteTTL: 25, + shutdownTTL: 30 + ); + } + + Fleet? m_Fleet; + + [Test] + public void ConstructFleetGetOutputWithValidFleet() + { + FleetGetOutput output = new(m_Fleet!); + Assert.Multiple(() => + { + Assert.That(output.Name, Is.EqualTo(m_Fleet!.Name)); + Assert.That(output.Id, Is.EqualTo(m_Fleet!.Id)); + Assert.That(output.OsFamily, Is.EqualTo(m_Fleet!.OsFamily)); + Assert.That(output.OsName, Is.EqualTo(m_Fleet!.OsName)); + Assert.That(output.Status, Is.EqualTo(m_Fleet!.Status)); + Assert.That(output.BuildConfigurations, Is.EqualTo(m_Fleet!.BuildConfigurations)); + Assert.That(output.FleetRegions, Is.EqualTo(m_Fleet!.FleetRegions)); + Assert.That(output.Servers, Is.EqualTo(m_Fleet!.Servers)); + Assert.That(output.AllocationTtl, Is.EqualTo(m_Fleet!.AllocationTTL)); + Assert.That(output.DeleteTtl, Is.EqualTo(m_Fleet!.DeleteTTL)); + Assert.That(output.DisabledDeleteTtl, Is.EqualTo(m_Fleet!.DisabledDeleteTTL)); + Assert.That(output.ShutdownTtl, Is.EqualTo(m_Fleet!.ShutdownTTL)); + }); + } + + [Test] + public void FleetGetOutputToString() + { + FleetGetOutput output = new(m_Fleet!); + var sb = new StringBuilder(); + sb.AppendLine("name: Fleet One"); + sb.AppendLine("id: 00000000-0000-0000-1000-000000000000"); + sb.AppendLine("osFamily: LINUX"); + sb.AppendLine("osName: Linux"); + sb.AppendLine("status: ONLINE"); + sb.AppendLine("buildConfigurations: []"); + sb.AppendLine("fleetRegions: []"); + sb.AppendLine("servers:"); + sb.AppendLine(" all:"); + sb.AppendLine(" status:"); + sb.AppendLine(" allocated: 0"); + sb.AppendLine(" available: 0"); + sb.AppendLine(" online: 0"); + sb.AppendLine(" total: 0"); + sb.AppendLine(" cloud:"); + sb.AppendLine(" status:"); + sb.AppendLine(" allocated: 0"); + sb.AppendLine(" available: 0"); + sb.AppendLine(" online: 0"); + sb.AppendLine(" total: 0"); + sb.AppendLine(" metal:"); + sb.AppendLine(" status:"); + sb.AppendLine(" allocated: 0"); + sb.AppendLine(" available: 0"); + sb.AppendLine(" online: 0"); + sb.AppendLine(" total: 0"); + sb.AppendLine("allocationTtl: 10"); + sb.AppendLine("deleteTtl: 20"); + sb.AppendLine("disabledDeleteTtl: 25"); + sb.AppendLine("shutdownTtl: 30"); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetListItemOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetListItemOutputTests.cs new file mode 100644 index 0000000..6b71dc1 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetListItemOutputTests.cs @@ -0,0 +1,77 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +public class FleetListItemOutputTests +{ + [SetUp] + public void SetUp() + { + m_Fleet = new FleetListItem( + allocationType: FleetListItem.AllocationTypeEnum.ALLOCATION, + new List(), + regions: new List(), + id: new Guid(ValidFleetId), + name: ValidFleetName, + osName: OsNameLinux, + servers: new Servers(new FleetServerBreakdown(new ServerStatus()), + new FleetServerBreakdown(new ServerStatus()), + new FleetServerBreakdown(new ServerStatus())), + status: FleetListItem.StatusEnum.ONLINE + ); + } + + FleetListItem? m_Fleet; + + [Test] + public void ConstructFleetListItemOutputWithValidList() + { + FleetListItemOutput output = new(m_Fleet!); + Assert.Multiple(() => + { + Assert.That(output.BuildConfigurations, Is.EqualTo(m_Fleet!.BuildConfigurations)); + Assert.That(output.Regions, Is.EqualTo(m_Fleet!.Regions)); + Assert.That(output.Id, Is.EqualTo(m_Fleet!.Id)); + Assert.That(output.Name, Is.EqualTo(m_Fleet!.Name)); + Assert.That(output.OsName, Is.EqualTo(m_Fleet!.OsName)); + Assert.That(output.Servers, Is.EqualTo(m_Fleet!.Servers)); + Assert.That(output.Status, Is.EqualTo(m_Fleet!.Status)); + }); + } + + [Test] + public void FleetListItemOutputToString() + { + FleetListItemOutput output = new(m_Fleet!); + var sb = new StringBuilder(); + sb.AppendLine("name: " + ValidFleetName); + sb.AppendLine("id: " + ValidFleetId); + sb.AppendLine("osName: " + OsNameLinux); + sb.AppendLine("status: " + FleetListItem.StatusEnum.ONLINE); + sb.AppendLine("buildConfigurations: []"); + sb.AppendLine("regions: []"); + sb.AppendLine("servers:"); + sb.AppendLine(" all:"); + sb.AppendLine(" status:"); + sb.AppendLine(" allocated: 0"); + sb.AppendLine(" available: 0"); + sb.AppendLine(" online: 0"); + sb.AppendLine(" total: 0"); + sb.AppendLine(" cloud:"); + sb.AppendLine(" status:"); + sb.AppendLine(" allocated: 0"); + sb.AppendLine(" available: 0"); + sb.AppendLine(" online: 0"); + sb.AppendLine(" total: 0"); + sb.AppendLine(" metal:"); + sb.AppendLine(" status:"); + sb.AppendLine(" allocated: 0"); + sb.AppendLine(" available: 0"); + sb.AppendLine(" online: 0"); + sb.AppendLine(" total: 0"); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetListOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetListOutputTests.cs new file mode 100644 index 0000000..f514b4f --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetListOutputTests.cs @@ -0,0 +1,98 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +class FleetListOutputTests +{ + [SetUp] + public void SetUp() + { + m_Fleets = new List + { + new( + allocationType: FleetListItem.AllocationTypeEnum.ALLOCATION, + new List(), + regions: new List(), + id: new Guid(ValidFleetId), + name: ValidFleetName, + osName: OsNameLinux, + servers: new Servers(new FleetServerBreakdown(new ServerStatus()), + new FleetServerBreakdown(new ServerStatus()), + new FleetServerBreakdown(new ServerStatus())), + status: FleetListItem.StatusEnum.ONLINE + ), + new( + allocationType: FleetListItem.AllocationTypeEnum.ALLOCATION, + new List(), + regions: new List(), + id: new Guid(ValidFleetId2), + name: ValidFleetName2, + osName: OsNameLinux, + servers: new Servers(new FleetServerBreakdown(new ServerStatus()), + new FleetServerBreakdown(new ServerStatus()), + new FleetServerBreakdown(new ServerStatus())), + status: FleetListItem.StatusEnum.ONLINE + ) + }; + } + + List? m_Fleets; + + [Test] + public void ConstructFleetListOutputWithValidList() + { + FleetListOutput output = new(m_Fleets!); + Assert.That(output, Has.Count.EqualTo(m_Fleets!.Count)); + for (var i = 0; i < output.Count; i++) + Assert.Multiple(() => + { + Assert.That(output[i].BuildConfigurations, Is.EqualTo(m_Fleets[i].BuildConfigurations)); + Assert.That(output[i].Regions, Is.EqualTo(m_Fleets[i].Regions)); + Assert.That(output[i].Id, Is.EqualTo(m_Fleets[i].Id)); + Assert.That(output[i].Name, Is.EqualTo(m_Fleets[i].Name)); + Assert.That(output[i].OsName, Is.EqualTo(m_Fleets[i].OsName)); + Assert.That(output[i].Servers, Is.EqualTo(m_Fleets[i].Servers)); + Assert.That(output[i].Status, Is.EqualTo(m_Fleets[i].Status)); + }); + } + + [Test] + public void FleetListOutputToString() + { + FleetListOutput output = new(m_Fleets!); + var sb = new StringBuilder(); + foreach (var fleet in output) + { + sb.AppendLine($"- name: {fleet.Name}"); + sb.AppendLine($" id: {fleet.Id}"); + sb.AppendLine($" osName: {fleet.OsName}"); + sb.AppendLine($" status: {fleet.Status}"); + sb.AppendLine(" buildConfigurations: []"); + sb.AppendLine(" regions: []"); + sb.AppendLine(" servers:"); + sb.AppendLine(" all:"); + sb.AppendLine(" status:"); + sb.AppendLine($" allocated: {fleet.Servers.All.Status.Allocated}"); + sb.AppendLine($" available: {fleet.Servers.All.Status.Available}"); + sb.AppendLine($" online: {fleet.Servers.All.Status.Online}"); + sb.AppendLine($" total: {fleet.Servers.All.Total}"); + sb.AppendLine(" cloud:"); + sb.AppendLine(" status:"); + sb.AppendLine($" allocated: {fleet.Servers.Cloud.Status.Allocated}"); + sb.AppendLine($" available: {fleet.Servers.Cloud.Status.Available}"); + sb.AppendLine($" online: {fleet.Servers.Cloud.Status.Online}"); + sb.AppendLine($" total: {fleet.Servers.Cloud.Total}"); + sb.AppendLine(" metal:"); + sb.AppendLine(" status:"); + sb.AppendLine($" allocated: {fleet.Servers.Metal.Status.Allocated}"); + sb.AppendLine($" available: {fleet.Servers.Metal.Status.Available}"); + sb.AppendLine($" online: {fleet.Servers.Metal.Status.Online}"); + sb.AppendLine($" total: {fleet.Servers.Metal.Total}"); + } + + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetRegionCreateOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetRegionCreateOutputTests.cs new file mode 100644 index 0000000..fbdfbcc --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetRegionCreateOutputTests.cs @@ -0,0 +1,51 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +class FleetRegionCreateOutputTests +{ + [SetUp] + public void Setup() + { + m_FleetRegion = new NewFleetRegion( + id: Guid.Parse(ValidFleetRegionId), + maxServers: 2, + minAvailableServers: 1, + regionID: Guid.Parse(ValidRegionId), + regionName: "RegionName" + ); + } + + NewFleetRegion? m_FleetRegion; + + [Test] + public void ConstructFleetRegionCreateOutputWithValidFleetRegion() + { + FleetRegionCreateOutput output = new(m_FleetRegion!); + Assert.Multiple(() => + { + Assert.That(output.FleetRegionId, Is.EqualTo(m_FleetRegion?.Id)); + Assert.That(output.MaxServers, Is.EqualTo(m_FleetRegion?.MaxServers)); + Assert.That(output.MinAvailableServers, Is.EqualTo(m_FleetRegion?.MinAvailableServers)); + Assert.That(output.RegionId, Is.EqualTo(m_FleetRegion?.RegionID)); + Assert.That(output.RegionName, Is.EqualTo(m_FleetRegion?.RegionName)); + }); + } + + [Test] + public void FleetRegionCreateOutputToString() + { + var sb = new StringBuilder(); + FleetRegionCreateOutput output = new(m_FleetRegion!); + + sb.AppendLine($"fleetRegionId: {output.FleetRegionId}"); + sb.AppendLine($"maxServers: {output.MaxServers}"); + sb.AppendLine($"minAvailableServers: {output.MinAvailableServers}"); + sb.AppendLine($"regionId: {output.RegionId}"); + sb.AppendLine($"regionName: {output.RegionName}"); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetRegionUpdateOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetRegionUpdateOutputTests.cs new file mode 100644 index 0000000..4021937 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/FleetRegionUpdateOutputTests.cs @@ -0,0 +1,64 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +class FleetRegionUpdateOutputTests +{ + [SetUp] + public void Setup() + { + m_FleetRegion = new UpdatedFleetRegion( + deleteTTL: 120, + disabledDeleteTTL: 60, + id: Guid.Parse(ValidFleetRegionId), + maxServers: 3, + minAvailableServers: 3, + regionID: Guid.Parse(ValidRegionId), + regionName: "RegionName", + scalingEnabled: false, + shutdownTTL: 180 + ); + } + + UpdatedFleetRegion? m_FleetRegion; + + [Test] + public void ConstructFleetRegionUpdateOutputWithValidFleetRegion() + { + FleetRegionUpdateOutput output = new(m_FleetRegion!); + Assert.Multiple(() => + { + Assert.That(output.DeleteTtl, Is.EqualTo(m_FleetRegion?.DeleteTTL)); + Assert.That(output.DisabledDeleteTtl, Is.EqualTo(m_FleetRegion?.DisabledDeleteTTL)); + Assert.That(output.Id, Is.EqualTo(m_FleetRegion?.Id)); + Assert.That(output.MaxServers, Is.EqualTo(m_FleetRegion?.MaxServers)); + Assert.That(output.MinAvailableServers, Is.EqualTo(m_FleetRegion?.MinAvailableServers)); + Assert.That(output.RegionId, Is.EqualTo(m_FleetRegion?.RegionID)); + Assert.That(output.RegionName, Is.EqualTo(m_FleetRegion?.RegionName)); + Assert.That(output.ScalingEnabled, Is.EqualTo(m_FleetRegion?.ScalingEnabled)); + Assert.That(output.ShutdownTtl, Is.EqualTo(m_FleetRegion?.ShutdownTTL)); + } + ); + } + + [Test] + public void FleetRegionUpdateOutputToString() + { + var sb = new StringBuilder(); + FleetRegionUpdateOutput output = new(m_FleetRegion!); + + sb.AppendLine($"deleteTtl: {output.DeleteTtl}"); + sb.AppendLine($"disabledDeleteTtl: {output.DisabledDeleteTtl}"); + sb.AppendLine($"id: {output.Id}"); + sb.AppendLine($"maxServers: {output.MaxServers}"); + sb.AppendLine($"minAvailableServers: {output.MinAvailableServers}"); + sb.AppendLine($"regionId: {output.RegionId}"); + sb.AppendLine($"regionName: {output.RegionName}"); + sb.AppendLine($"scalingEnabled: {output.ScalingEnabled.ToString().ToLower()}"); + sb.AppendLine($"shutdownTtl: {output.ShutdownTtl}"); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/RegionListOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/RegionListOutputTests.cs new file mode 100644 index 0000000..3a6858c --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/RegionListOutputTests.cs @@ -0,0 +1,54 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +public class RegionListOutputTests +{ + [SetUp] + public void SetUp() + { + m_Regions = new List + { + new( + regionID: new Guid(ValidTemplateRegionId), + name: ValidTemplateRegionName + ), + new( + regionID: new Guid(ValidTemplateRegionId2), + name: ValidTemplateRegionName2 + ) + }; + } + + List? m_Regions; + + [Test] + public void ConstructTemplateRegionListOutputWithValidList() + { + RegionTemplateListOutput output = new(m_Regions!); + Assert.That(output, Has.Count.EqualTo(m_Regions!.Count)); + for (var i = 0; i < output.Count; i++) + Assert.Multiple(() => + { + Assert.That(output[i].Name, Is.EqualTo(m_Regions[i].Name)); + Assert.That(output[i].RegionId, Is.EqualTo(m_Regions[i].RegionID)); + }); + } + + [Test] + public void TemplateRegionListOutputToString() + { + RegionTemplateListOutput output = new(m_Regions!); + var sb = new StringBuilder(); + foreach (var region in output) + { + sb.AppendLine("- name: " + region.Name); + sb.AppendLine(" regionId: " + region.RegionId); + } + + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/RegionOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/RegionOutputTests.cs new file mode 100644 index 0000000..fdc908b --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/RegionOutputTests.cs @@ -0,0 +1,41 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +public class RegionOutputTests +{ + [SetUp] + public void SetUp() + { + m_RegionItem = new FleetRegionsTemplateListItem( + regionID: new Guid(ValidTemplateRegionId), + name: ValidTemplateRegionName + ); + } + + FleetRegionsTemplateListItem? m_RegionItem; + + [Test] + public void ConstructFleetListItemOutputWithValidList() + { + RegionTemplateListItemOutput output = new(m_RegionItem!); + Assert.Multiple(() => + { + Assert.That(output.Name, Is.EqualTo(m_RegionItem!.Name)); + Assert.That(output.RegionId, Is.EqualTo(m_RegionItem!.RegionID)); + }); + } + + [Test] + public void TemplateRegionListItemOutputToString() + { + RegionTemplateListItemOutput output = new(m_RegionItem!); + var sb = new StringBuilder(); + sb.AppendLine("name: " + ValidTemplateRegionName); + sb.AppendLine("regionId: " + ValidTemplateRegionId); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/RegionTemplateListOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/RegionTemplateListOutputTests.cs new file mode 100644 index 0000000..98fccaa --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/RegionTemplateListOutputTests.cs @@ -0,0 +1,41 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +public class RegionTemplateListOutputTests +{ + [SetUp] + public void SetUp() + { + m_RegionItem = new FleetRegionsTemplateListItem( + regionID: new Guid(ValidTemplateRegionId), + name: ValidTemplateRegionName + ); + } + + FleetRegionsTemplateListItem? m_RegionItem; + + [Test] + public void ConstructFleetListItemOutputWithValidList() + { + RegionTemplateListItemOutput output = new(m_RegionItem!); + Assert.Multiple(() => + { + Assert.That(output.Name, Is.EqualTo(m_RegionItem!.Name)); + Assert.That(output.RegionId, Is.EqualTo(m_RegionItem!.RegionID)); + }); + } + + [Test] + public void TemplateRegionListItemOutputToString() + { + RegionTemplateListItemOutput output = new(m_RegionItem!); + var sb = new StringBuilder(); + sb.AppendLine("name: " + ValidTemplateRegionName); + sb.AppendLine("regionId: " + ValidTemplateRegionId); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/ServerGetOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/ServerGetOutputTests.cs new file mode 100644 index 0000000..9b34bcc --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/ServerGetOutputTests.cs @@ -0,0 +1,81 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +public class ServerGetOutputTests +{ + Server? m_Server; + + + [SetUp] + public void SetUp() + { + m_Server = new Server( + buildConfigurationID: ValidBuildConfigurationId, + buildConfigurationName: "buildConfigurationName", + buildName: ValidBuildName, + deleted: false, + fleetID: new Guid(ValidFleetId), + fleetName: "fleetName", + hardwareType: Server.HardwareTypeEnum.METAL, + id: 1, + ip: "192.168.1.1", + locationID: 3, + locationName: "locationName", + machineName: "test machine", + machineSpec: new MachineSpec("test-cpu"), + machineID: 5, + port: 440, + status: Server.StatusEnum.READY + ); + } + + [Test] + public void ConstructServerGetOutput() + { + ServerGetOutput output = new ServerGetOutput(m_Server!); + Assert.Multiple(() => + { + Assert.That(output.BuildConfigurationId, Is.EqualTo(m_Server!.BuildConfigurationID)); + Assert.That(output.BuildConfigurationName, Is.EqualTo(m_Server!.BuildConfigurationName)); + Assert.That(output.BuildName, Is.EqualTo(m_Server!.BuildName)); + Assert.That(output.Deleted, Is.EqualTo(m_Server!.Deleted)); + Assert.That(output.FleetId, Is.EqualTo(m_Server!.FleetID)); + Assert.That(output.FleetName, Is.EqualTo(m_Server!.FleetName)); + Assert.That(output.HardwareType, Is.EqualTo(m_Server!.HardwareType)); + Assert.That(output.Id, Is.EqualTo(m_Server!.Id)); + Assert.That(output.Ip, Is.EqualTo(m_Server!.Ip)); + Assert.That(output.LocationId, Is.EqualTo(m_Server!.LocationID)); + Assert.That(output.LocationName, Is.EqualTo(m_Server!.LocationName)); + Assert.That(output.MachineId, Is.EqualTo(m_Server!.MachineID)); + Assert.That(output.Port, Is.EqualTo(m_Server!.Port)); + Assert.That(output.Status, Is.EqualTo(m_Server!.Status)); + }); + + } + + [Test] + public void ServerGetOutputToString() + { + ServerGetOutput output = new(m_Server!); + var sb = new StringBuilder(); + sb.AppendLine("buildConfigurationId: 1"); + sb.AppendLine("buildConfigurationName: buildConfigurationName"); + sb.AppendLine("buildName: Build One"); + sb.AppendLine("deleted: false"); + sb.AppendLine("fleetId: 00000000-0000-0000-1000-000000000000"); + sb.AppendLine("fleetName: fleetName"); + sb.AppendLine("hardwareType: METAL"); + sb.AppendLine("id: 1"); + sb.AppendLine("ip: 192.168.1.1"); + sb.AppendLine("locationId: 3"); + sb.AppendLine("locationName: locationName"); + sb.AppendLine("machineId: 5"); + sb.AppendLine("port: 440"); + sb.AppendLine("status: READY"); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/ServersItemOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/ServersItemOutputTests.cs new file mode 100644 index 0000000..9dacc82 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/ServersItemOutputTests.cs @@ -0,0 +1,75 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +public class ServersItemOutputTests +{ + [SetUp] + public void SetUp() + { + m_Server = new Server( + id: ValidServerId, + ip: "0.0.0.0", + port: 9000, + machineID: ValidMachineId, + machineName: "test machine", + machineSpec: new MachineSpec("test-cpu"), + locationID: ValidLocationId, + locationName: ValidLocationName, + fleetID: new Guid(ValidFleetId), + fleetName: ValidFleetName, + buildConfigurationID: ValidBuildConfigurationId, + buildConfigurationName: ValidBuildConfigurationName, + buildName: ValidBuildName, + deleted: false + ); + } + + Server? m_Server; + + [Test] + public void ConstructServersItemOutputWithValidInput() + { + ServersItemOutput output = new(m_Server!); + Assert.Multiple( + () => + { + Assert.That(output.Id, Is.EqualTo(m_Server!.Id)); + Assert.That(output.Ip, Is.EqualTo(m_Server!.Ip)); + Assert.That(output.Port, Is.EqualTo(m_Server!.Port)); + Assert.That(output.MachineId, Is.EqualTo(m_Server!.MachineID)); + Assert.That(output.LocationId, Is.EqualTo(m_Server!.LocationID)); + Assert.That(output.LocationName, Is.EqualTo(m_Server!.LocationName)); + Assert.That(output.FleetId, Is.EqualTo(m_Server!.FleetID)); + Assert.That(output.FleetName, Is.EqualTo(m_Server!.FleetName)); + Assert.That(output.BuildConfigurationId, Is.EqualTo(m_Server!.BuildConfigurationID)); + Assert.That(output.BuildConfigurationName, Is.EqualTo(m_Server!.BuildConfigurationName)); + Assert.That(output.BuildName, Is.EqualTo(m_Server!.BuildName)); + Assert.That(output.Deleted, Is.EqualTo(m_Server!.Deleted)); + } + ); + } + + [Test] + public void ServersItemOutputToString() + { + ServersItemOutput output = new(m_Server!); + var sb = new StringBuilder(); + sb.AppendLine($"id: {ValidServerId}"); + sb.AppendLine("ip: 0.0.0.0"); + sb.AppendLine("port: 9000"); + sb.AppendLine($"machineId: {ValidMachineId}"); + sb.AppendLine($"locationName: {ValidLocationName}"); + sb.AppendLine($"locationId: {ValidLocationId}"); + sb.AppendLine($"fleetName: {ValidFleetName}"); + sb.AppendLine($"fleetId: {ValidFleetId}"); + sb.AppendLine($"buildConfigurationName: {ValidBuildConfigurationName}"); + sb.AppendLine($"buildConfigurationId: {ValidBuildConfigurationId}"); + sb.AppendLine($"buildName: {ValidBuildName}"); + sb.AppendLine("deleted: false"); + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/ServersOutputTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/ServersOutputTests.cs new file mode 100644 index 0000000..e299f1a --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Model/ServersOutputTests.cs @@ -0,0 +1,86 @@ +using System.Text; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Model; + +[TestFixture] +public class ServersOutputTests +{ + [SetUp] + public void SetUp() + { + m_Servers = new List + { + new( + id: ValidServerId, + ip: "0.0.0.0", + port: 9000, + machineID: ValidMachineId, + machineName: "test machine", + machineSpec: new MachineSpec("test-cpu"), + locationID: ValidLocationId, + locationName: ValidLocationName, + fleetID: new Guid(ValidFleetId), + fleetName: ValidFleetName, + buildConfigurationID: ValidBuildConfigurationId, + buildConfigurationName: ValidBuildConfigurationName, + buildName: ValidBuildName, + deleted: false + ) + }; + } + + List? m_Servers; + + [Test] + public void ConstructServersOutputWithValidInput() + { + ServersOutput output = new(m_Servers!); + Assert.That(output, Has.Count.EqualTo(m_Servers!.Count)); + for (var i = 0; i < output.Count; i++) + { + Assert.Multiple( + () => + { + Assert.That(output[i].Id, Is.EqualTo(m_Servers[i].Id)); + Assert.That(output[i].Ip, Is.EqualTo(m_Servers[i].Ip)); + Assert.That(output[i].Port, Is.EqualTo(m_Servers[i].Port)); + Assert.That(output[i].MachineId, Is.EqualTo(m_Servers[i].MachineID)); + Assert.That(output[i].LocationId, Is.EqualTo(m_Servers[i].LocationID)); + Assert.That(output[i].LocationName, Is.EqualTo(m_Servers[i].LocationName)); + Assert.That(output[i].FleetId, Is.EqualTo(m_Servers[i].FleetID)); + Assert.That(output[i].FleetName, Is.EqualTo(m_Servers[i].FleetName)); + Assert.That(output[i].BuildConfigurationId, Is.EqualTo(m_Servers[i].BuildConfigurationID)); + Assert.That(output[i].BuildConfigurationName, Is.EqualTo(m_Servers[i].BuildConfigurationName)); + Assert.That(output[i].BuildName, Is.EqualTo(m_Servers[i].BuildName)); + Assert.That(output[i].Deleted, Is.EqualTo(m_Servers[i].Deleted)); + } + ); + } + } + + [Test] + public void ServersOutputToString() + { + ServersOutput output = new(m_Servers!); + var sb = new StringBuilder(); + foreach (var server in output) + { + sb.AppendLine($"- id: {server.Id}"); + sb.AppendLine(" ip: 0.0.0.0"); + sb.AppendLine(" port: 9000"); + sb.AppendLine($" machineId: {server.MachineId}"); + sb.AppendLine($" locationName: {server.LocationName}"); + sb.AppendLine($" locationId: {server.LocationId}"); + sb.AppendLine($" fleetName: {server.FleetName}"); + sb.AppendLine($" fleetId: {server.FleetId}"); + sb.AppendLine($" buildConfigurationName: {server.BuildConfigurationName}"); + sb.AppendLine($" buildConfigurationId: {server.BuildConfigurationId}"); + sb.AppendLine($" buildName: {server.BuildName}"); + sb.AppendLine(" deleted: false"); + } + + Assert.That(output.ToString(), Is.EqualTo(sb.ToString())); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Service/GameServerHostingServiceTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Service/GameServerHostingServiceTests.cs new file mode 100644 index 0000000..f9637eb --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Service/GameServerHostingServiceTests.cs @@ -0,0 +1,68 @@ +using Moq; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Cli.GameServerHosting.UnitTest.Mocks; +using Unity.Services.Cli.ServiceAccountAuthentication; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Service; + +[TestFixture] +class GameServerHostingServiceTests +{ + [SetUp] + public void SetUp() + { + m_AuthenticationService = new Mock(); + m_AuthenticationService.Setup(a => a.GetAccessTokenAsync(CancellationToken.None)) + .Returns(Task.FromResult(TestAccessToken)); + + m_BuildsApi = new GameServerHostingBuildsApiV1Mock(); + m_BuildsApi.SetUp(); + + m_FleetsApi = new GameServerHostingFleetsApiV1Mock(); + m_FleetsApi.SetUp(); + + m_ServersApi = new GameServerHostingServersApiV1Mock(); + m_ServersApi.SetUp(); + + m_BuildConfigurationsApi = new GameServerHostingBuildConfigurationsApiV1Mock(); + m_BuildConfigurationsApi.SetUp(); + + m_GameServerHostingService = new GameServerHostingService( + m_AuthenticationService.Object, + m_BuildsApi.DefaultBuildsClient.Object, + m_BuildConfigurationsApi.DefaultBuildConfigurationsClient.Object, + m_FleetsApi.DefaultFleetsClient.Object, + m_ServersApi.DefaultServersClient.Object + ); + } + + Mock? m_AuthenticationService; + GameServerHostingBuildsApiV1Mock? m_BuildsApi; + GameServerHostingFleetsApiV1Mock? m_FleetsApi; + GameServerHostingServersApiV1Mock? m_ServersApi; + GameServerHostingService? m_GameServerHostingService; + GameServerHostingBuildConfigurationsApiV1Mock? m_BuildConfigurationsApi; + + [Test] + public async Task AuthorizeGameServerHostingService() + { + await m_GameServerHostingService!.AuthorizeGameServerHostingService(CancellationToken.None); + m_AuthenticationService!.Verify(a => a.GetAccessTokenAsync(CancellationToken.None), Times.Once); + Assert.Multiple( + () => + { + Assert.That( + m_BuildsApi!.DefaultBuildsClient.Object.Configuration.DefaultHeaders["Authorization"], + Is.EqualTo($"Basic {TestAccessToken}")); + Assert.That( + m_FleetsApi!.DefaultFleetsClient.Object.Configuration.DefaultHeaders["Authorization"], + Is.EqualTo($"Basic {TestAccessToken}")); + Assert.That( + m_ServersApi!.DefaultServersClient.Object.Configuration.DefaultHeaders["Authorization"], + Is.EqualTo($"Basic {TestAccessToken}")); + Assert.That( + m_BuildConfigurationsApi!.DefaultBuildConfigurationsClient.Object.Configuration.DefaultHeaders["Authorization"], + Is.EqualTo($"Basic {TestAccessToken}")); + }); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/ApiClientFactoryTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/ApiClientFactoryTests.cs new file mode 100644 index 0000000..9e9109e --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/ApiClientFactoryTests.cs @@ -0,0 +1,116 @@ +using Moq; +using Unity.Services.Cli.GameServerHosting.Services; +using Unity.Services.Cli.ServiceAccountAuthentication; +using Unity.Services.Gateway.ContentDeliveryManagementApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; +using Unity.Services.Multiplay.Authoring.Core.Builds; +using Unity.Services.Multiplay.Authoring.Core.MultiplayApi; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Services; + +public class ApiClientFactoryTests +{ + Dictionary m_Headers = new(); + GameServerHostingApiConfig m_ApiConfig = new(); + Mock? m_MockBuildsApi; + Mock? m_MockBuildConfigApi; + Mock? m_MockFleetsApi; + Mock? m_MockBucketsApi; + Mock? m_MockEntriesApi; + Mock? m_MockAuthentication; + + [SetUp] + public void SetUp() + { + var gameServerHostingConfig = new Mock(); + gameServerHostingConfig.SetupGet(c => c.DefaultHeaders).Returns(m_Headers); + + var ccdConfig = new Mock(); + ccdConfig.SetupGet(c => c.DefaultHeaders).Returns(m_Headers); + + m_MockBuildsApi = new Mock(); + m_MockBuildsApi.SetupGet(a => a.Configuration).Returns(gameServerHostingConfig.Object); + + m_MockBuildConfigApi = new Mock(); + m_MockBuildConfigApi.SetupGet(a => a.Configuration).Returns(gameServerHostingConfig.Object); + + m_MockFleetsApi = new Mock(); + m_MockFleetsApi.SetupGet(a => a.Configuration).Returns(gameServerHostingConfig.Object); + + m_MockBucketsApi = new Mock(); + m_MockBucketsApi.SetupGet(a => a.Configuration).Returns(ccdConfig.Object); + + m_MockEntriesApi = new Mock(); + m_MockEntriesApi.SetupGet(a => a.Configuration).Returns(ccdConfig.Object); + + m_MockAuthentication = new Mock(); + } + + [Test] + public async Task GameServerHostingApiIBuildsApiFactoryBuild_Authenticates() + { + IBuildsApiFactory factory = new ApiClientFactory( + m_MockBuildsApi!.Object, + m_MockBuildConfigApi!.Object, + m_MockFleetsApi!.Object, + m_MockBucketsApi!.Object, + m_MockEntriesApi!.Object, + m_MockAuthentication!.Object, + m_ApiConfig); + + await factory.Build(); + + Assert.That(() => m_Headers.ContainsKey("Authorization")); + } + + [Test] + public async Task GameServerHostingApiIBuildConfigurationApiFactoryBuild_Authenticates() + { + IBuildConfigApiFactory factory = new ApiClientFactory( + m_MockBuildsApi!.Object, + m_MockBuildConfigApi!.Object, + m_MockFleetsApi!.Object, + m_MockBucketsApi!.Object, + m_MockEntriesApi!.Object, + m_MockAuthentication!.Object, + m_ApiConfig); + + await factory.Build(); + + Assert.That(() => m_Headers.ContainsKey("Authorization")); + } + + [Test] + public async Task GameServerHostingApiIFleetsApiFactoryBuild_Authenticates() + { + IFleetApiFactory factory = new ApiClientFactory( + m_MockBuildsApi!.Object, + m_MockBuildConfigApi!.Object, + m_MockFleetsApi!.Object, + m_MockBucketsApi!.Object, + m_MockEntriesApi!.Object, + m_MockAuthentication!.Object, + m_ApiConfig); + + await factory.Build(); + + Assert.That(() => m_Headers.ContainsKey("Authorization")); + } + + [Test] + public async Task CcdCloudStorageApiFactoryBuild_Authenticates() + { + ICloudStorageFactory factory = new ApiClientFactory( + m_MockBuildsApi!.Object, + m_MockBuildConfigApi!.Object, + m_MockFleetsApi!.Object, + m_MockBucketsApi!.Object, + m_MockEntriesApi!.Object, + m_MockAuthentication!.Object, + m_ApiConfig); + + await factory.Build(); + + Assert.That(() => m_Headers.ContainsKey("Authorization")); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/BuildClientTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/BuildClientTests.cs new file mode 100644 index 0000000..af2da33 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/BuildClientTests.cs @@ -0,0 +1,133 @@ +using Moq; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Services; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using Unity.Services.Multiplay.Authoring.Core.Assets; +using Unity.Services.Multiplay.Authoring.Core.Builds; +using Unity.Services.Multiplay.Authoring.Core.MultiplayApi; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Services; + +public class BuildClientTests +{ + Mock? m_MockApi; + BuildClient? m_Client; + + [SetUp] + public void SetUp() + { + m_MockApi = new Mock(); + m_Client = new BuildClient(m_MockApi.Object, new GameServerHostingApiConfig()); + } + + [Test] + public async Task FindByName_WithNoResults_ReturnsNull() + { + SetupListResponse(new List()); + + var res = await m_Client!.FindByName("test"); + + Assert.That(res, Is.Null); + } + + [Test] + public async Task FindByName_WithOneResult_ReturnsId() + { + SetupListResponse(new List + { + new (0, 0, "test", ccd: new CCDDetails()) + }); + + var res = await m_Client!.FindByName("test"); + + Assert.That(res, Is.Not.Null); + } + + [Test] + public void FindByName_WithMultipleResults_ThrowsDuplicateException() + { + SetupListResponse(new List + { + new (0, 0, "test", ccd: new CCDDetails()), + new (0, 0, "test", ccd: new CCDDetails()) + }); + + Assert.ThrowsAsync(async () => await m_Client!.FindByName("test")); + } + + [Test] + public async Task Create_CallsCreateBuildAsync() + { + m_MockApi!.Setup(a => + a.CreateBuildAsync(Guid.Empty, Guid.Empty, It.IsAny(), default, default)) + .ReturnsAsync(new CreateBuild200Response(buildName: "test", ccd: new CCDDetails())); + + await m_Client!.Create("test", new MultiplayConfig.BuildDefinition()); + + m_MockApi!.Verify(a => + a.CreateBuildAsync(Guid.Empty, Guid.Empty, It.IsAny(), default, default)); + } + + [Test] + public async Task CreateVersion_CallsCreateNewBuildVersionAsync() + { + await m_Client!.CreateVersion(new BuildId(), new CloudBucketId()); + + m_MockApi!.Verify(a => + a.CreateNewBuildVersionAsync(Guid.Empty, Guid.Empty, 0, It.IsAny(), default, default)); + } + + [Test] + public async Task IsSynced_SucceedsWhenSynced() + { + m_MockApi!.Setup(c => c.GetBuildAsync(Guid.Empty, Guid.Empty, 0, default, default)) + .ReturnsAsync(new CreateBuild200Response( + buildName: string.Empty, + syncStatus: CreateBuild200Response.SyncStatusEnum.SYNCED)); + + Assert.That(await m_Client!.IsSynced(new BuildId { Id = 0 })); + } + + [Test] + public async Task IsSynced_FailsWhenNotSynced() + { + m_MockApi!.Setup(c => c.GetBuildAsync(Guid.Empty, Guid.Empty, 0, default, default)) + .ReturnsAsync(new CreateBuild200Response( + buildName: string.Empty, + syncStatus: CreateBuild200Response.SyncStatusEnum.SYNCING)); + + Assert.That(await m_Client!.IsSynced(new BuildId { Id = 0 }), Is.Not.True); + } + + [Test] + public void IsSynced_ThrowsOnFailure() + { + m_MockApi!.Setup(c => c.GetBuildAsync(Guid.Empty, Guid.Empty, 0, default, default)) + .ReturnsAsync(new CreateBuild200Response( + buildName: string.Empty, + syncStatus: CreateBuild200Response.SyncStatusEnum.FAILED)); + + Assert.ThrowsAsync(() => m_Client!.IsSynced(new BuildId + { + Id = 0 + })); + } + + void SetupListResponse(List results) + { + m_MockApi!.Setup(a => + a.ListBuildsAsync( + Guid.Empty, + Guid.Empty, + null, + null, + null, + null, + null, + It.IsAny(), + default, + default)) + .ReturnsAsync(results); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/BuildConfigsClientTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/BuildConfigsClientTests.cs new file mode 100644 index 0000000..5889fa3 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/BuildConfigsClientTests.cs @@ -0,0 +1,134 @@ +using Moq; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Services; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using Unity.Services.Multiplay.Authoring.Core.Assets; +using Unity.Services.Multiplay.Authoring.Core.MultiplayApi; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Services; + +public class BuildConfigsClientTests +{ + Mock? m_MockApi; + BuildConfigsClient? m_Client; + + [SetUp] + public void SetUp() + { + m_MockApi = new Mock(); + m_Client = new BuildConfigsClient(m_MockApi.Object, new GameServerHostingApiConfig()); + } + + [Test] + public async Task FindByName_WithNoResults_ReturnsNull() + { + m_MockApi!.Setup(a => + a.ListBuildConfigurationsAsync( + Guid.Empty, + Guid.Empty, + null, + "test", + default, + default)) + .ReturnsAsync(new List()); + + var res = await m_Client!.FindByName("test"); + + Assert.That(res, Is.Null); + } + + [Test] + public async Task FindByName_WithOneResult_ReturnsId() + { + m_MockApi!.Setup(a => + a.ListBuildConfigurationsAsync( + Guid.Empty, + Guid.Empty, + null, + "test", + default, + default)) + .ReturnsAsync(new List + { + new (0, string.Empty, DateTime.Now, updatedAt: DateTime.Now, name: "test") + }); + + var res = await m_Client!.FindByName("test"); + + Assert.That(res, Is.Not.Null); + } + + [Test] + public void FindByName_WithMultipleResults_ThrowsDuplicateException() + { + m_MockApi!.Setup(a => + a.ListBuildConfigurationsAsync( + Guid.Empty, + Guid.Empty, + null, + "test", + default, + default)) + .ReturnsAsync(new List + { + new (0, string.Empty, DateTime.Now, updatedAt: DateTime.Now, name: "test"), + new (0, string.Empty, DateTime.Now, updatedAt: DateTime.Now, name: "test") + }); + + Assert.ThrowsAsync(async () => await m_Client!.FindByName("test")); + } + + [Test] + public async Task Create_CallsCreateApi() + { + m_MockApi!.Setup(a => + a.CreateBuildConfigurationAsync( + Guid.Empty, + Guid.Empty, + It.IsAny(), + default, + default)) + .ReturnsAsync(new BuildConfiguration( + string.Empty, + 0, + string.Empty, + string.Empty, + new List(), + name: "test", + queryType: "sqp")); + + await m_Client!.Create("test", new BuildId(), new MultiplayConfig.BuildConfigurationDefinition + { + BinaryPath = string.Empty, + CommandLine = string.Empty + }); + + m_MockApi!.Verify(a => + a.CreateBuildConfigurationAsync( + Guid.Empty, + Guid.Empty, + It.IsAny(), + default, + default)); + } + + [Test] + public async Task Update_CallsUpdateApi() + { + await m_Client!.Update(new BuildConfigurationId(), "test", new BuildId(), new MultiplayConfig.BuildConfigurationDefinition + { + BinaryPath = string.Empty, + CommandLine = string.Empty + }); + + m_MockApi!.Verify(a => + a.UpdateBuildConfigurationAsync( + Guid.Empty, + Guid.Empty, + 0, + It.IsAny(), + default, + default)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/CcdCloudStorageTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/CcdCloudStorageTests.cs new file mode 100644 index 0000000..34d40a8 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/CcdCloudStorageTests.cs @@ -0,0 +1,125 @@ +using System.Net; +using System.Text; +using Moq; +using Moq.Protected; +using Unity.Services.Cli.GameServerHosting.Services; +using Unity.Services.Gateway.ContentDeliveryManagementApiV1.Generated.Api; +using Unity.Services.Gateway.ContentDeliveryManagementApiV1.Generated.Model; +using Unity.Services.Multiplay.Authoring.Core.Builds; +using HttpClient = System.Net.Http.HttpClient; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Services; + +public class CcdCloudStorageTests +{ + CcdCloudStorageClient? m_CcdCloudStorage; + + Mock? m_MockBucketsApi; + Mock? m_MockEntriesApi; + Mock? m_MockMessageHandler; + + [SetUp] + public void SetUp() + { + m_MockBucketsApi = new Mock(MockBehavior.Strict); + m_MockEntriesApi = new Mock(MockBehavior.Strict); + m_MockMessageHandler = new Mock(MockBehavior.Strict); + + m_CcdCloudStorage = new CcdCloudStorageClient( + m_MockBucketsApi.Object, + m_MockEntriesApi.Object, + new HttpClient(m_MockMessageHandler.Object), + new GameServerHostingApiConfig()); + } + + [Test] + public async Task FindBucket_LoadsBucketByName() + { + var bucketId = Guid.NewGuid(); + var bucketName = "bucket"; + m_MockBucketsApi!.Setup(api => api.ListBucketsByProjectEnvAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new List { new CcdBucket(id: bucketId, name: bucketName) })); + + var bucket = await m_CcdCloudStorage!.FindBucket("test"); + + Assert.That(bucket.ToGuid(), Is.EqualTo(bucketId)); + } + + [Test] + public async Task CreateBucket_CreatesNewBucket() + { + var bucketId = Guid.NewGuid(); + var bucketName = "bucket"; + m_MockBucketsApi!.Setup(api => api.CreateBucketByProjectEnvAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new CcdBucket(id: bucketId, name: bucketName))); + + var bucket = await m_CcdCloudStorage!.CreateBucket("test"); + + Assert.That(bucket.ToGuid(), Is.EqualTo(bucketId)); + } + + [Test] + public async Task UploadBuildEntries_UploadsNewEntries() + { + SetupUpload(new List()); + + await m_CcdCloudStorage!.UploadBuildEntries( + new CloudBucketId { Id = Guid.NewGuid() }, + new List + { + new BuildEntry("path", new MemoryStream(Encoding.UTF8.GetBytes("content"))) + }); + + m_MockEntriesApi!.Verify(api => api.CreateOrUpdateEntryByPathEnvAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); + } + + [Test] + public async Task UploadBuildEntries_WhenExactFileExists_DoesNotUpload() + { + SetupUpload(new List + { + // Uppercase hash to ensure that case differences are handled + new CcdEntry(path: "path", contentHash: "9a0364b9e99bb480dd25e1f0284c8555".ToUpperInvariant()) + }); + + await m_CcdCloudStorage!.UploadBuildEntries( + new CloudBucketId { Id = Guid.NewGuid() }, + new List + { + new BuildEntry("path", new MemoryStream(Encoding.UTF8.GetBytes("content"))) + }); + + m_MockEntriesApi!.Verify(api => api.CreateOrUpdateEntryByPathEnvAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never()); + } + + [Test] + public async Task UploadBuildEntries_WhenOrphanExists_DeletesOrphan() + { + SetupUpload(new List + { + new CcdEntry(path: "path", contentHash: "hash") + }); + + await m_CcdCloudStorage!.UploadBuildEntries( + new CloudBucketId { Id = Guid.NewGuid() }, + new List()); + + m_MockEntriesApi!.Verify(api => api.DeleteEntryEnvAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); + } + + void SetupUpload(List ccdEntries) + { + m_MockEntriesApi!.Setup(api => api.GetEntriesEnvAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(ccdEntries)); + m_MockEntriesApi.Setup(api => api.CreateOrUpdateEntryByPathEnvAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new CcdEntry(signedUrl: "https://signed.url.example.com"))); + m_MockEntriesApi.Setup(api => api.DeleteEntryEnvAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(Task.FromResult(new List())); + m_MockMessageHandler.Protected() + .Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny()) + .Returns(Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK))); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/CcdHashExtensionsTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/CcdHashExtensionsTests.cs new file mode 100644 index 0000000..f52f221 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/CcdHashExtensionsTests.cs @@ -0,0 +1,17 @@ +using System.Text; +using System.Text.RegularExpressions; +using Unity.Services.Multiplay.Authoring.Core.CloudContent; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Services; + +public class CcdHashExtensionsTests +{ + [Test] + public void Hash_ReturnsLowerHex() + { + var data = new MemoryStream(Encoding.UTF8.GetBytes("hello world!")); + var hash = data.CcdHash(); + + Assert.IsTrue(Regex.IsMatch(hash, "^[a-fA-F0-9]+$")); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/DeployFileServiceTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/DeployFileServiceTests.cs new file mode 100644 index 0000000..ee0431b --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/DeployFileServiceTests.cs @@ -0,0 +1,91 @@ +using System.IO.Abstractions; +using Moq; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Services; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Services; + +public class DeployFileServiceTests +{ + readonly Mock m_MockFile = new(); + readonly Mock m_MockDirectory = new(); + readonly DeployFileService m_Service; + + public DeployFileServiceTests() + { + m_Service = new DeployFileService(m_MockFile.Object, m_MockDirectory.Object); + } + + [SetUp] + public void SetUp() + { + m_MockFile.Reset(); + m_MockDirectory.Reset(); + } + + [Test] + public void ReadAllTextAsync_ReadsFromFile() + { + m_Service.ReadAllTextAsync("test", CancellationToken.None); + + m_MockFile.Verify(f => f.ReadAllTextAsync("test", default), Times.Once); + } + + [Test] + public void ListFilesToDeploy_ReturnsExistingFiles() + { + m_MockFile.Setup(f => f.Exists("test.gsh")).Returns(true); + + var files = m_Service.ListFilesToDeploy(new List { "test.gsh" }, ".gsh"); + + Assert.That(files, Contains.Item("test.gsh")); + } + + [Test] + public void ListFilesToDeploy_WhenExtensionIsInvalid_Throws() + { + m_MockFile.Setup(f => f.Exists("test.foobar")).Returns(true); + + Assert.Throws(() => + { + var _ = m_Service.ListFilesToDeploy(new List + { + "test.foobar" + }, ".gsh").ToList(); + }); + } + + [Test] + public void ListFilesToDeploy_WhenDirectoryAndFileIsMissing_Throws() + { + Assert.Throws(() => + { + var _ = m_Service.ListFilesToDeploy(new List + { + "does_not_exist" + }, ".gsh").ToList(); + }); + } + + [Test] + public void ListFilesToDeploy_EnumeratesDirectories() + { + m_MockDirectory.Setup(f => f.Exists("foo")).Returns(true); + m_MockDirectory.Setup(d => d.EnumerateFiles("foo", "*.gsh", SearchOption.AllDirectories)) + .Returns(new List { "test.gsh" }); + + var files = m_Service.ListFilesToDeploy(new List { "foo" }, ".gsh"); + + Assert.That(files, Contains.Item("test.gsh")); + } + + [Test] + public void ListFilesToDeploy_OnEmptyInput_UsesCurrentDirectory() + { + Assert.Throws(() => + { + var _ = m_Service.ListFilesToDeploy(new List(), ".gsh").ToList(); + }); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/DummyBinaryBuilderTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/DummyBinaryBuilderTests.cs new file mode 100644 index 0000000..261a33c --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/DummyBinaryBuilderTests.cs @@ -0,0 +1,24 @@ +using Unity.Services.Cli.GameServerHosting.Services; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Services; + +public class DummyBinaryBuilderTests +{ + [Test] + public void BuildLinuxServer_ReturnsPath() + { + var build = new DummyBinaryBuilder().BuildLinuxServer("dir", "name"); + + Assert.That(build.Path.Contains("dir")); + Assert.That(build.Path.Contains("name")); + } + + [Test] + public void RevertToOriginalBuildTarget_DoesNothing() + { + Assert.DoesNotThrow(() => + { + new DummyBinaryBuilder().RevertToOriginalBuildTarget(); + }); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/FileReaderAdapterTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/FileReaderAdapterTests.cs new file mode 100644 index 0000000..4c6e0a3 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/FileReaderAdapterTests.cs @@ -0,0 +1,35 @@ +using System.IO.Abstractions; +using Moq; +using Unity.Services.Cli.GameServerHosting.Services; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Services; + +public class FileReaderAdapterTests +{ + readonly Mock m_MockFiles = new(); + readonly Mock m_MockDirectory = new(); + + [Test] + public void EnumerateDirectories_CallsEnumerateDirectories() + { + new FileReaderAdapter(m_MockFiles.Object, m_MockDirectory.Object).EnumerateDirectories("test"); + + m_MockDirectory.Verify(m => m.EnumerateDirectories("test"), Times.Once); + } + + [Test] + public void EnumerateFiles_CallsEnumerateFiles() + { + new FileReaderAdapter(m_MockFiles.Object, m_MockDirectory.Object).EnumerateFiles("test"); + + m_MockDirectory.Verify(m => m.EnumerateFiles("test"), Times.Once); + } + + [Test] + public void OpenReadFile_CallsOpenReadFile() + { + new FileReaderAdapter(m_MockFiles.Object, m_MockDirectory.Object).OpenReadFile("test"); + + m_MockFiles.Verify(m => m.OpenRead("test"), Times.Once); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/FleetsClientTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/FleetsClientTests.cs new file mode 100644 index 0000000..280bdb4 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/FleetsClientTests.cs @@ -0,0 +1,266 @@ +using Moq; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Services; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using Unity.Services.Multiplay.Authoring.Core.Assets; +using Unity.Services.Multiplay.Authoring.Core.MultiplayApi; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Services; + +public class FleetsClientTests +{ + Mock? m_MockApi; + FleetClient? m_Client; + + [SetUp] + public void SetUp() + { + m_MockApi = new Mock(); + m_Client = new FleetClient(m_MockApi.Object, new GameServerHostingApiConfig()); + } + + [Test] + public async Task FindByName_WithNoResults_ReturnsNull() + { + m_MockApi!.Setup(a => + a.ListFleetsAsync( + Guid.Empty, + Guid.Empty, + default, + default)) + .ReturnsAsync(new List()); + + var res = await m_Client!.FindByName("test"); + + Assert.That(res, Is.Null); + } + + [Test] + public async Task FindByName_WithOneResult_ReturnsId() + { + m_MockApi!.Setup(a => + a.ListFleetsAsync( + Guid.Empty, + Guid.Empty, + default, + default)) + .ReturnsAsync(new List + { + new FleetListItem( + allocationType: FleetListItem.AllocationTypeEnum.ALLOCATION, + new List(), + Guid.Empty, + name: "test", + osName: string.Empty, + regions: new List(), + servers: new Servers( + all: new FleetServerBreakdown(new ServerStatus()), + cloud: new FleetServerBreakdown(new ServerStatus()), + metal: new FleetServerBreakdown(new ServerStatus()) + ) + ) + }); + + var res = await m_Client!.FindByName("test"); + + Assert.That(res, Is.Not.Null); + } + + [Test] + public void FindByName_WithMultipleResults_ThrowsDuplicateException() + { + m_MockApi!.Setup(a => + a.ListFleetsAsync( + Guid.Empty, + Guid.Empty, + default, + default)) + .ReturnsAsync(new List + { + new FleetListItem( + allocationType: FleetListItem.AllocationTypeEnum.ALLOCATION, + new List(), + Guid.Empty, + name: "test", + osName: string.Empty, + regions: new List(), + servers: new Servers( + all: new FleetServerBreakdown(new ServerStatus()), + cloud: new FleetServerBreakdown(new ServerStatus()), + metal: new FleetServerBreakdown(new ServerStatus()) + ) + ), + new FleetListItem( + allocationType: FleetListItem.AllocationTypeEnum.ALLOCATION, + new List(), + Guid.Empty, + name: "test", + osName: string.Empty, + regions: new List(), + servers: new Servers( + all: new FleetServerBreakdown(new ServerStatus()), + cloud: new FleetServerBreakdown(new ServerStatus()), + metal: new FleetServerBreakdown(new ServerStatus()) + ) + ) + }); + + Assert.ThrowsAsync(async () => await m_Client!.FindByName("test")); + } + + [Test] + public async Task Create_CallsCreateApi() + { + m_MockApi!.Setup(a => + a.ListTemplateFleetRegionsAsync(Guid.Empty, Guid.Empty, default, default)) + .ReturnsAsync(new List()); + + m_MockApi.Setup(a => + a.CreateFleetAsync(Guid.Empty, Guid.Empty, It.IsAny(), default, default)) + .ReturnsAsync(new Fleet( + buildConfigurations: new List(), + fleetRegions: new List(), + name: "test", + osName: string.Empty, + servers: new Servers( + all: new FleetServerBreakdown(new ServerStatus()), + cloud: new FleetServerBreakdown(new ServerStatus()), + metal: new FleetServerBreakdown(new ServerStatus()) + ))); + + await m_Client!.Create("test", new List(), new MultiplayConfig.FleetDefinition()); + + m_MockApi.Verify(a => + a.CreateFleetAsync(Guid.Empty, Guid.Empty, It.IsAny(), default, default)); + } + + [Test] + public async Task Update_CallsTheUpdateApi() + { + m_MockApi!.Setup(a => + a.ListTemplateFleetRegionsAsync(Guid.Empty, Guid.Empty, default, default)) + .ReturnsAsync(new List()); + + m_MockApi.Setup(a => + a.UpdateFleetAsync(Guid.Empty, Guid.Empty, Guid.Empty, It.IsAny(), default, default)) + .ReturnsAsync(new Fleet( + buildConfigurations: new List(), + fleetRegions: new List(), + name: "test", + osName: string.Empty, + servers: new Servers( + all: new FleetServerBreakdown(new ServerStatus()), + cloud: new FleetServerBreakdown(new ServerStatus()), + metal: new FleetServerBreakdown(new ServerStatus()) + ))); + + await m_Client!.Update(new FleetId(), "test", new List(), new MultiplayConfig.FleetDefinition()); + + m_MockApi.Verify(a => + a.UpdateFleetAsync(Guid.Empty, Guid.Empty, Guid.Empty, It.IsAny(), default, default)); + } + + [Test] + public async Task Update_RemovesOldFleets() + { + var regionId = Guid.NewGuid(); + m_MockApi!.Setup(a => + a.ListTemplateFleetRegionsAsync(Guid.Empty, Guid.Empty, default, default)) + .ReturnsAsync(new List()); + + m_MockApi.Setup(a => + a.UpdateFleetAsync(Guid.Empty, Guid.Empty, Guid.Empty, It.IsAny(), default, default)) + .ReturnsAsync(new Fleet( + buildConfigurations: new List(), + fleetRegions: new List() + { + new (regionID: regionId, regionName: string.Empty) + }, + name: "test", + osName: string.Empty, + servers: new Servers( + all: new FleetServerBreakdown(new ServerStatus()), + cloud: new FleetServerBreakdown(new ServerStatus()), + metal: new FleetServerBreakdown(new ServerStatus()) + ))); + + await m_Client!.Update(new FleetId(), "test", new List(), new MultiplayConfig.FleetDefinition()); + + m_MockApi.Verify(f => f.UpdateFleetRegionAsync(Guid.Empty, Guid.Empty, Guid.Empty, regionId, null, default, default)); + } + + [Test] + public async Task Update_AddsNewFleets() + { + var regionId = Guid.NewGuid(); + m_MockApi!.Setup(a => + a.ListTemplateFleetRegionsAsync(Guid.Empty, Guid.Empty, default, default)) + .ReturnsAsync(new List + { + new ("North-America", regionId) + }); + + m_MockApi.Setup(a => + a.UpdateFleetAsync(Guid.Empty, Guid.Empty, Guid.Empty, It.IsAny(), default, default)) + .ReturnsAsync(new Fleet( + buildConfigurations: new List(), + fleetRegions: new List(), + name: "test", + osName: string.Empty, + servers: new Servers( + all: new FleetServerBreakdown(new ServerStatus()), + cloud: new FleetServerBreakdown(new ServerStatus()), + metal: new FleetServerBreakdown(new ServerStatus()) + ))); + + await m_Client!.Update(new FleetId(), "test", new List(), new MultiplayConfig.FleetDefinition + { + Regions = new Dictionary + { + {"North-America", new MultiplayConfig.ScalingDefinition()} + } + }); + + m_MockApi.Verify(f => + f.AddFleetRegionAsync(Guid.Empty, Guid.Empty, Guid.Empty, It.IsAny(), default, default)); + } + + [Test] + public async Task Update_UpdatesExistingFleets() + { + var regionId = Guid.NewGuid(); + m_MockApi!.Setup(a => + a.ListTemplateFleetRegionsAsync(Guid.Empty, Guid.Empty, default, default)) + .ReturnsAsync(new List + { + new ("North-America", regionId) + }); + + m_MockApi.Setup(a => + a.UpdateFleetAsync(Guid.Empty, Guid.Empty, Guid.Empty, It.IsAny(), default, default)) + .ReturnsAsync(new Fleet( + buildConfigurations: new List(), + fleetRegions: new List + { + new (regionID: regionId, regionName: "North-America") + }, + name: "test", + osName: string.Empty, + servers: new Servers( + all: new FleetServerBreakdown(new ServerStatus()), + cloud: new FleetServerBreakdown(new ServerStatus()), + metal: new FleetServerBreakdown(new ServerStatus()) + ))); + + await m_Client!.Update(new FleetId(), "test", new List(), new MultiplayConfig.FleetDefinition + { + Regions = new Dictionary + { + {"North-America", new MultiplayConfig.ScalingDefinition()} + } + }); + + m_MockApi.Verify(f => f.UpdateFleetRegionAsync(Guid.Empty, Guid.Empty, Guid.Empty, regionId, It.IsAny(), default, default)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/GameServerHostingConfigLoaderTests.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/GameServerHostingConfigLoaderTests.cs new file mode 100644 index 0000000..f3eda1e --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Services/GameServerHostingConfigLoaderTests.cs @@ -0,0 +1,135 @@ +using Microsoft.Extensions.Logging; +using Moq; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Services; +using Unity.Services.Multiplay.Authoring.Core.Assets; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.UnitTest.Services; + +public class GameServerHostingConfigLoaderTests +{ + readonly Mock m_MockDeployFileService = new(); + readonly Mock m_MockValidator = new(); + readonly Mock m_MockLogger = new(); + + readonly GameServerHostingConfigLoader m_Loader; + + readonly MultiplayConfig m_TestFile = new() + { + Builds = new Dictionary + { + { + new BuildName + { + Name = "test" + }, + new MultiplayConfig.BuildDefinition() + } + } + }; + + public GameServerHostingConfigLoaderTests() + { + m_Loader = new GameServerHostingConfigLoader(m_MockDeployFileService.Object, m_MockValidator.Object, m_MockLogger.Object); + } + + [SetUp] + public void SetUp() + { + m_MockValidator.Reset(); + m_MockLogger.Reset(); + m_MockValidator.Setup(v => v.Validate(It.IsAny())) + .Returns(new List()); + } + + [Test] + public async Task Deploy_LoadsSingleConfig() + { + SetupInputPaths("test.gsh"); + SetupFiles(new Dictionary + { + { "test.gsh", m_TestFile }, + }, CancellationToken.None); + + var config = await m_Loader.LoadAndValidateAsync(new List + { + "test.gsh" + }, CancellationToken.None); + + Assert.That(config.Builds, Contains.Key(new BuildName { Name = "test" })); + } + + [Test] + public void Deploy_WhenValidationFails_ThrowsValidationException() + { + SetupInputPaths("test.gsh"); + SetupFiles(new Dictionary { { "test.gsh", m_TestFile } }, CancellationToken.None); + m_MockValidator.Setup(m => m.Validate(It.IsAny())) + .Returns(new List + { + new ("fail") + }); + + Assert.ThrowsAsync(async () => + { + await m_Loader.LoadAndValidateAsync(new List + { + "test.gsh" + }, CancellationToken.None); + }); + } + + [Test] + public async Task Deploy_LoadsMultipleConfigs() + { + SetupInputPaths("test.gsh", "test2.gsh"); + SetupFiles(new Dictionary + { + { "test.gsh", m_TestFile }, + { @"test2.gsh", new MultiplayConfig + { + Builds = new Dictionary + { + { new BuildName{ Name = "bar" }, new MultiplayConfig.BuildDefinition() } + } + } + }, + }, CancellationToken.None); + + var config = await m_Loader.LoadAndValidateAsync(new List + { + "foo" + }, CancellationToken.None); + + Assert.That(config.Builds, Contains.Key(new BuildName { Name = "test" })); + Assert.That(config.Builds, Contains.Key(new BuildName { Name = "bar" })); + } + + void SetupInputPaths(params string[] paths) + { + m_MockDeployFileService.Setup(l => l.ListFilesToDeploy(It.IsAny>(), It.IsAny())) + .Returns(new List(paths)); + } + + void SetupFiles(Dictionary fileSystem, CancellationToken cancellationToken) + { + foreach (var (path, config) in fileSystem) + { + m_MockDeployFileService.Setup(l => l.ReadAllTextAsync(path, cancellationToken)) + .ReturnsAsync(ConfigFile(config)); + } + } + + static string ConfigFile(MultiplayConfig config) + { + var deserializer = new SerializerBuilder() + .WithTypeConverter(new ResourceNameTypeConverter()) + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .Build(); + + return deserializer.Serialize(config); + } + +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Unity.Services.Cli.GameServerHosting.UnitTest.csproj b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Unity.Services.Cli.GameServerHosting.UnitTest.csproj new file mode 100644 index 0000000..9b6e004 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Unity.Services.Cli.GameServerHosting.UnitTest.csproj @@ -0,0 +1,24 @@ + + + net6.0 + enable + enable + false + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + \ No newline at end of file diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Usings.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Usings.cs new file mode 100644 index 0000000..bb81274 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting.UnitTest/Usings.cs @@ -0,0 +1,2 @@ +global using NUnit.Framework; +global using static Unity.Services.Cli.GameServerHosting.UnitTest.GameServerHostingUnitTestsConstants; diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Endpoints/CloudContentDeliveryEndpoints.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Endpoints/CloudContentDeliveryEndpoints.cs new file mode 100644 index 0000000..be8fab6 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Endpoints/CloudContentDeliveryEndpoints.cs @@ -0,0 +1,10 @@ +using Unity.Services.Cli.Common.Networking; + +namespace Unity.Services.Cli.GameServerHosting.Endpoints; + +public class CloudContentDeliveryEndpoints : NetworkTargetEndpoints +{ + protected override string Prod { get; } = "https://content-api.cloud.unity3d.com/api/v1"; + + protected override string Staging { get; } = "https://content-api-stg.cloud.unity3d.com/api/v1"; +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/DuplicateResourceException.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/DuplicateResourceException.cs new file mode 100644 index 0000000..aa087c8 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/DuplicateResourceException.cs @@ -0,0 +1,15 @@ +using System.Runtime.Serialization; +using Unity.Services.Cli.Common.Exceptions; + +namespace Unity.Services.Cli.GameServerHosting.Exceptions; + +[Serializable] +public class DuplicateResourceException : CliException +{ + public DuplicateResourceException(string resource, string name) + : base($"found duplicate {resource} of name {name}", Common.Exceptions.ExitCode.HandledError) + { + } + + protected DuplicateResourceException(SerializationInfo info, StreamingContext context) : base(info, context) { } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidConfigException.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidConfigException.cs new file mode 100644 index 0000000..b31fee6 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidConfigException.cs @@ -0,0 +1,13 @@ +using System.Runtime.Serialization; +using Unity.Services.Cli.Common.Exceptions; + +namespace Unity.Services.Cli.GameServerHosting.Exceptions; + +[Serializable] +public class InvalidConfigException : CliException +{ + public InvalidConfigException(string path) + : base($"Game Server Hosting Config file is invalid. See output for details: {path}", Common.Exceptions.ExitCode.HandledError) { } + + protected InvalidConfigException(SerializationInfo info, StreamingContext context) : base(info, context) { } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidExtensionException.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidExtensionException.cs new file mode 100644 index 0000000..1728a63 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidExtensionException.cs @@ -0,0 +1,13 @@ +using System.Runtime.Serialization; +using Unity.Services.Cli.Common.Exceptions; + +namespace Unity.Services.Cli.GameServerHosting.Exceptions; + +[Serializable] +public class InvalidExtensionException : CliException +{ + public InvalidExtensionException(string path, string extension) + : base($"File path must end in '{extension}': {path}", Common.Exceptions.ExitCode.HandledError) { } + + protected InvalidExtensionException(SerializationInfo info, StreamingContext context) : base(info, context) { } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidKeyValuePairException.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidKeyValuePairException.cs new file mode 100644 index 0000000..d5e08c6 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidKeyValuePairException.cs @@ -0,0 +1,12 @@ +using System.Runtime.Serialization; +using Unity.Services.Cli.Common.Exceptions; + +namespace Unity.Services.Cli.GameServerHosting.Exceptions; + +[Serializable] +public class InvalidKeyValuePairException : CliException +{ + protected InvalidKeyValuePairException(SerializationInfo info, StreamingContext context) : base(info, context) { } + public InvalidKeyValuePairException(string input) + : base($"Could not parse key:value pair from input: '{input}'", Common.Exceptions.ExitCode.HandledError) { } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidResponseException.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidResponseException.cs new file mode 100644 index 0000000..26d120f --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/InvalidResponseException.cs @@ -0,0 +1,12 @@ +using System.Runtime.Serialization; +using Unity.Services.Cli.Common.Exceptions; + +namespace Unity.Services.Cli.GameServerHosting.Exceptions; + +[Serializable] +public class InvalidResponseException : CliException +{ + protected InvalidResponseException(SerializationInfo info, StreamingContext context) : base(info, context) { } + public InvalidResponseException(string reason) + : base($"Response is invalid: '{reason}'", Common.Exceptions.ExitCode.HandledError) { } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/MissingInputException.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/MissingInputException.cs new file mode 100644 index 0000000..0ded253 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/MissingInputException.cs @@ -0,0 +1,12 @@ +using System.Runtime.Serialization; +using Unity.Services.Cli.Common.Exceptions; + +namespace Unity.Services.Cli.GameServerHosting.Exceptions; + +[Serializable] +public class MissingInputException : CliException +{ + protected MissingInputException(SerializationInfo info, StreamingContext context) : base(info, context) { } + public MissingInputException(string input) + : base($"Missing value for input: '{input}'", Common.Exceptions.ExitCode.HandledError) { } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/PathNotFoundException.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/PathNotFoundException.cs new file mode 100644 index 0000000..a9a425a --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/PathNotFoundException.cs @@ -0,0 +1,13 @@ +using System.Runtime.Serialization; +using Unity.Services.Cli.Common.Exceptions; + +namespace Unity.Services.Cli.GameServerHosting.Exceptions; + +[Serializable] +public class PathNotFoundException : CliException +{ + public PathNotFoundException(string path) + : base($"File path {path} could not be found.", Common.Exceptions.ExitCode.HandledError) { } + + protected PathNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context) { } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/SyncFailedException.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/SyncFailedException.cs new file mode 100644 index 0000000..7472bc4 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Exceptions/SyncFailedException.cs @@ -0,0 +1,15 @@ +using System.Runtime.Serialization; +using Unity.Services.Cli.Common.Exceptions; + +namespace Unity.Services.Cli.GameServerHosting.Exceptions; + +[Serializable] +public class SyncFailedException : CliException +{ + public SyncFailedException() + : base("failed to sync build", Common.Exceptions.ExitCode.HandledError) + { + } + + protected SyncFailedException(SerializationInfo info, StreamingContext context) : base(info, context) { } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/GameServerHostingFeatures.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/GameServerHostingFeatures.cs new file mode 100644 index 0000000..156cd40 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/GameServerHostingFeatures.cs @@ -0,0 +1,8 @@ +namespace Unity.Services.Cli.GameServerHosting; + +static class GameServerHostingFeatures +{ + public const string Root = "GameServerHosting"; + public const string Build = "GameServerHostingBuild"; + public const string Authoring = "GameServerHostingAuthoring"; +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/GameServerHostingModule.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/GameServerHostingModule.cs new file mode 100644 index 0000000..04a7c84 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/GameServerHostingModule.cs @@ -0,0 +1,645 @@ +using System.CommandLine; +using System.IO.Abstractions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Input; +using Unity.Services.Cli.Common.Networking; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Endpoints; +using Unity.Services.Cli.GameServerHosting.Handlers; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Cli.GameServerHosting.Services; +using Unity.Services.Cli.ServiceAccountAuthentication; +using Unity.Services.Gateway.ContentDeliveryManagementApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Client; +using Unity.Services.Multiplay.Authoring.Core; +using Unity.Services.Multiplay.Authoring.Core.Assets; +using Unity.Services.Multiplay.Authoring.Core.Builds; +using Unity.Services.Multiplay.Authoring.Core.Deployment; +using Unity.Services.Multiplay.Authoring.Core.MultiplayApi; +using GameServerHostingConfiguration = Unity.Services.Gateway.GameServerHostingApiV1.Generated.Client.Configuration; +using CloudContentDeliveryConfiguration = + Unity.Services.Gateway.ContentDeliveryManagementApiV1.Generated.Client.Configuration; +using IBuildsApi = Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api.IBuildsApi; + +namespace Unity.Services.Cli.GameServerHosting; + +public class GameServerHostingModule : ICommandModule +{ + public GameServerHostingModule() + { + BuildCreateCommand = new Command("create", "Create a Game Server Hosting build.") + { + BuildCreateInput.BuildNameOption, + BuildCreateInput.BuildOsFamilyOption, + BuildCreateInput.BuildTypeOption, + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption + }; + BuildCreateCommand.SetHandler< + BuildCreateInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, ILoadingIndicator, + CancellationToken + >(BuildCreateHandler.BuildCreateAsync); + + BuildCreateVersionCommand = new Command("create-version", "Create a new version of a Game Server Hosting build.") + { + BuildCreateVersionInput.BuildIdArgument, + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption, + BuildCreateVersionInput.AccessKeyOption, + BuildCreateVersionInput.BucketUrlOption, + BuildCreateVersionInput.ContainerTagOption, + BuildCreateVersionInput.FileDirectoryOption, + BuildCreateVersionInput.SecretKeyOption, + BuildCreateVersionInput.RemoveOldFilesOption + }; + BuildCreateVersionCommand.SetHandler< + BuildCreateVersionInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + HttpClient, + ILoadingIndicator, + CancellationToken + >(BuildCreateVersionHandler.BuildCreateVersionAsync); + + + BuildDeleteCommand = new Command("delete", "Delete a Game Server Hosting build") + { + BuildIdInput.BuildIdArgument, + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption + }; + BuildDeleteCommand.SetHandler< + BuildIdInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(BuildDeleteHandler.BuildDeleteAsync); + + BuildGetCommand = new Command("get", "Get a Game Server Hosting build.") + { + BuildIdInput.BuildIdArgument, + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption + }; + BuildGetCommand.SetHandler< + BuildIdInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, ILoadingIndicator, + CancellationToken + >(BuildGetHandler.BuildGetAsync); + + BuildInstallsCommand = new Command("installs", "List Game Server Hosting build installs") + { + BuildIdInput.BuildIdArgument, + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption + }; + BuildInstallsCommand.SetHandler< + BuildIdInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(BuildInstallsHandler.BuildInstallsAsync); + + BuildListCommand = new Command("list", "List Game Server Hosting builds.") + { + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption + }; + BuildListCommand.SetHandler< + CommonInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, ILoadingIndicator, + CancellationToken + >(BuildListHandler.BuildListAsync); + + BuildUpdateCommand = new Command("update", "Update a Game Server Hosting build") + { + BuildIdInput.BuildIdArgument, + BuildUpdateInput.BuildNameOption, + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption + }; + BuildUpdateCommand.SetHandler< + BuildUpdateInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, ILoadingIndicator, + CancellationToken + >(BuildUpdateHandler.BuildUpdateAsync); + + BuildCommand = new Command("build", "Manage Game Server Hosting builds.") + { + BuildCreateCommand, + BuildCreateVersionCommand, + BuildDeleteCommand, + BuildGetCommand, + BuildInstallsCommand, + BuildListCommand, + BuildUpdateCommand + }; + + BuildConfigurationGetCommand = new Command("get", "Get a Game Server Hosting build configurations.") + { + BuildConfigurationIdInput.BuildConfigurationIdArgument, + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption + }; + BuildConfigurationGetCommand.SetHandler< + BuildConfigurationIdInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(BuildConfigurationGetHandler.BuildConfigurationGetAsync); + + BuildConfigurationCreateCommand = new Command("create", "Create a Game Server Hosting build configuration.") + { + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption, + BuildConfigurationCreateInput.BinaryPathOption, + BuildConfigurationCreateInput.BuildIdOption, + BuildConfigurationCreateInput.CommandLineOption, + BuildConfigurationCreateInput.ConfigurationOption, + BuildConfigurationCreateInput.CoresOption, + BuildConfigurationCreateInput.MemoryOption, + BuildConfigurationCreateInput.NameOption, + BuildConfigurationCreateInput.QueryTypeOption, + BuildConfigurationCreateInput.SpeedOption, + }; + BuildConfigurationCreateCommand.SetHandler< + BuildConfigurationCreateInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(BuildConfigurationCreateHandler.BuildConfigurationCreateAsync); + + BuildConfigurationDeleteCommand = new Command("delete", "Delete a Game Server Hosting build configurations.") + { + BuildConfigurationIdInput.BuildConfigurationIdArgument, + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption + }; + BuildConfigurationDeleteCommand.SetHandler< + BuildConfigurationIdInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(BuildConfigurationDeleteHandler.BuildConfigurationDeleteAsync); + + BuildConfigurationListCommand = new Command("list", "List Game Server Hosting build configurations.") + { + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption, + BuildConfigurationListInput.FleetIdOption, + BuildConfigurationListInput.PartialOption + }; + BuildConfigurationListCommand.SetHandler< + BuildConfigurationListInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(BuildConfigurationListHandler.BuildConfigurationListAsync); + + BuildConfigurationUpdateCommand = new Command("update", "Update a Game Server Hosting build configuration.") + { + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption, + BuildConfigurationUpdateInput.BuildConfigIdArgument, + BuildConfigurationUpdateInput.BinaryPathOption, + BuildConfigurationUpdateInput.BuildIdOption, + BuildConfigurationUpdateInput.CommandLineOption, + BuildConfigurationUpdateInput.ConfigurationOption, + BuildConfigurationUpdateInput.CoresOption, + BuildConfigurationUpdateInput.MemoryOption, + BuildConfigurationUpdateInput.NameOption, + BuildConfigurationUpdateInput.QueryTypeOption, + BuildConfigurationUpdateInput.SpeedOption, + }; + BuildConfigurationUpdateCommand.SetHandler< + BuildConfigurationUpdateInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(BuildConfigurationUpdateHandler.BuildConfigurationUpdateAsync); + + BuildConfigurationCommand = new Command("build-configuration", "Manage Game Server Hosting build configurations.") + { + BuildConfigurationGetCommand, + BuildConfigurationCreateCommand, + BuildConfigurationDeleteCommand, + BuildConfigurationListCommand, + BuildConfigurationUpdateCommand, + }; + + FleetCreateCommand = new Command("create", "Create Game Server Hosting fleet.") + { + FleetCreateInput.FleetNameOption, + FleetCreateInput.FleetOsFamilyOption, + FleetCreateInput.FleetRegionsOption, + FleetCreateInput.FleetBuildConfigurationsOption, + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption + }; + FleetCreateCommand.SetHandler< + FleetCreateInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(FleetCreateHandler.FleetCreateAsync); + + + FleetDeleteCommand = new Command("delete", "Delete a Game Server Hosting fleet.") + { + FleetIdInput.FleetIdArgument, + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption + }; + FleetDeleteCommand.SetHandler< + FleetIdInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(FleetDeleteHandler.FleetDeleteAsync); + + FleetGetCommand = new Command("get", "Get a Game Server Hosting fleet.") + { + FleetIdInput.FleetIdArgument, + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption + }; + FleetGetCommand.SetHandler< + FleetIdInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(FleetGetHandler.FleetGetAsync); + + FleetListCommand = new Command("list", "List Game Server Hosting fleets.") + { + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption + }; + FleetListCommand.SetHandler< + CommonInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(FleetListHandler.FleetListAsync); + + FleetUpdateCommand = new Command("update", "Update a Game Server Hosting fleet.") + { + FleetIdInput.FleetIdArgument, + FleetUpdateInput.FleetNameOption, + FleetUpdateInput.AllocTtlOption, + FleetUpdateInput.DeleteTtlOption, + FleetUpdateInput.DisabledDeleteTtlOption, + FleetUpdateInput.ShutdownTtlOption, + FleetUpdateInput.BuildConfigsOption, + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption + }; + FleetUpdateCommand.SetHandler< + FleetUpdateInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(FleetUpdateHandler.FleetUpdateAsync); + + FleetCommand = new Command("fleet", "Manage Game Server Hosting fleets.") + { + FleetCreateCommand, + FleetDeleteCommand, + FleetGetCommand, + FleetListCommand, + FleetUpdateCommand + }; + + + FleetRegionTemplatesCommand = new Command("templates", "List Game Server Hosting templates for creating fleet regions.") + { + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption + }; + FleetRegionTemplatesCommand.SetHandler< + CommonInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(RegionTemplatesHandler.RegionTemplatesAsync); + + FleetRegionAvailableCommand = new Command("available", "List Game Server Hosting available template regions for creating fleet regions.") + { + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption, + FleetIdInput.FleetIdArgument + }; + FleetRegionAvailableCommand.SetHandler< + FleetIdInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(RegionAvailableHandler.RegionAvailableAsync); + + FleetRegionCreateCommand = new Command("create", "Create Game Server Hosting fleet regions.") + { + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption, + FleetRegionCreateInput.FleetIdOption, + FleetRegionCreateInput.RegionIdOption, + FleetRegionCreateInput.MinAvailableServersOption, + FleetRegionCreateInput.MaxServersOption + }; + FleetRegionCreateCommand.SetHandler< + FleetRegionCreateInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(FleetRegionCreateHandler.FleetRegionCreateAsync); + + FleetRegionUpdateCommand = new Command("update", "Update Game Server Hosting fleet region.") + { + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption, + FleetRegionUpdateInput.FleetIdOption, + FleetRegionUpdateInput.RegionIdOption, + FleetRegionUpdateInput.MinAvailableServersOption, + FleetRegionUpdateInput.MaxServersOption + }; + + FleetRegionUpdateCommand.SetHandler< + FleetRegionUpdateInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(FleetRegionUpdateHandler.FleetRegionUpdateAsync); + + FleetRegionCommand = new Command("fleet-region", "Manage Game Server Hosting fleet regions.") + { + FleetRegionTemplatesCommand, + FleetRegionAvailableCommand, + FleetRegionCreateCommand, + FleetRegionUpdateCommand + }; + + ServerGetCommand = new Command("get", "Get a Game Server Hosting server.") + { + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption, + ServerIdInput.ServerIdArgument + }; + ServerGetCommand.SetHandler< + ServerIdInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(ServerGetHandler.ServerGetAsync); + + ServerListCommand = new Command("list", "List Game Server Hosting servers.") + { + CommonInput.EnvironmentNameOption, + CommonInput.CloudProjectIdOption, + ServerListInput.FleetIdOption, + ServerListInput.BuildConfigurationIdOption, + ServerListInput.PartialOption, + ServerListInput.StatusOption + }; + ServerListCommand.SetHandler< + ServerListInput, + IUnityEnvironment, + IGameServerHostingService, + ILogger, + ILoadingIndicator, + CancellationToken + >(ServerListHandler.ServerListAsync); + + ServerCommand = new Command("server", "Manage Game Server Hosting servers.") + { + ServerGetCommand, + ServerListCommand + }; + + ModuleRootCommand = new Command("game-server-hosting", "Manage Game Sever Hosting resources.") + { + BuildCommand, + BuildConfigurationCommand, + FleetCommand, + FleetRegionCommand, + ServerCommand + + }; + + ModuleRootCommand.AddAlias("gsh"); + BuildCommand.AddAlias("b"); + BuildConfigurationCommand.AddAlias("bc"); + FleetCommand.AddAlias("f"); + FleetRegionCommand.AddAlias("fr"); + ServerCommand.AddAlias("s"); + } + + internal Command BuildCommand { get; } + internal Command BuildConfigurationCommand { get; } + internal Command FleetCommand { get; } + internal Command FleetRegionCommand { get; } + internal Command ServerCommand { get; } + + // Build Commands + internal Command BuildCreateCommand { get; } + internal Command BuildCreateVersionCommand { get; } + internal Command BuildDeleteCommand { get; } + internal Command BuildGetCommand { get; } + internal Command BuildInstallsCommand { get; } + internal Command BuildListCommand { get; } + internal Command BuildUpdateCommand { get; } + + // Build Configuration Commands + internal Command BuildConfigurationGetCommand { get; } + internal Command BuildConfigurationCreateCommand { get; } + internal Command BuildConfigurationListCommand { get; } + internal Command BuildConfigurationUpdateCommand { get; } + internal Command BuildConfigurationDeleteCommand { get; } + + // Fleet Commands + internal Command FleetCreateCommand { get; } + internal Command FleetDeleteCommand { get; } + internal Command FleetGetCommand { get; } + internal Command FleetListCommand { get; } + internal Command FleetUpdateCommand { get; } + + // Fleet Region Commands + internal Command FleetRegionAvailableCommand { get; } + internal Command FleetRegionTemplatesCommand { get; } + internal Command FleetRegionCreateCommand { get; } + internal Command FleetRegionUpdateCommand { get; } + + // Server Commands + internal Command ServerGetCommand { get; } + internal Command ServerListCommand { get; } + + + internal static ExceptionFactory ExceptionFactory => (method, response) => + { + var message = (string text) => $"Error calling {method}: {text}"; + + // Handle errors from the backend + var statusCode = (int)response.StatusCode; + if (statusCode >= 400) + { + return new ApiException( + statusCode, + message(response.RawContent), + response.RawContent, + response.Headers + ); + } + + // Handle errors from the client, such as serialization errors + // These errors were being discarded before, but we want to surface them + if (!string.IsNullOrEmpty(response.ErrorText)) + { + return new ApiException( + statusCode, + message(response.ErrorText), + response.Content, + response.Headers + ); + } + + return null!; + }; + + // GSH Module Command + public Command ModuleRootCommand { get; } + + public static void RegisterServices(HostBuilderContext _, IServiceCollection serviceCollection) + { + var serviceProvider = serviceCollection.BuildServiceProvider(); + var authenticationService = serviceProvider.GetRequiredService(); + + var gameServerHostingConfiguration = new GameServerHostingConfiguration + { + BasePath = EndpointHelper.GetCurrentEndpointFor() + }; + gameServerHostingConfiguration.DefaultHeaders.SetXClientIdHeader(); + + IBuildsApi buildsApi = new BuildsApi(gameServerHostingConfiguration) + { + ExceptionFactory = ExceptionFactory + }; + IBuildConfigurationsApi buildConfigurationsApi = new BuildConfigurationsApi(gameServerHostingConfiguration) + { + ExceptionFactory = ExceptionFactory + }; + IFleetsApi fleetsApi = new FleetsApi(gameServerHostingConfiguration) + { + ExceptionFactory = ExceptionFactory + }; + IServersApi serversApi = new ServersApi(gameServerHostingConfiguration) + { + ExceptionFactory = ExceptionFactory + }; + + GameServerHostingService service = new( + authenticationService, + buildsApi, + buildConfigurationsApi, + fleetsApi, + serversApi + ); + + serviceCollection.AddSingleton(service); + + serviceCollection.AddTransient(_ => new FileSystem().File); + serviceCollection.AddTransient(_ => new FileSystem().Directory); + + RegisterApiClients(serviceCollection); + RegisterAuthoringServices(serviceCollection); + } + + static void RegisterApiClients(IServiceCollection serviceCollection) + { + var gameServerHostingConfig = new GameServerHostingConfiguration + { + BasePath = EndpointHelper.GetCurrentEndpointFor() + }; + gameServerHostingConfig.DefaultHeaders.SetXClientIdHeader(); + serviceCollection.AddSingleton(new BuildsApi(gameServerHostingConfig)); + serviceCollection.AddSingleton( + new BuildConfigurationsApi(gameServerHostingConfig)); + serviceCollection.AddSingleton(new FleetsApi(gameServerHostingConfig)); + + var cloudContentDeliveryConfiguration = new CloudContentDeliveryConfiguration + { + BasePath = EndpointHelper.GetCurrentEndpointFor() + }; + cloudContentDeliveryConfiguration.DefaultHeaders.SetXClientIdHeader(); + serviceCollection.AddSingleton(new BucketsApi(cloudContentDeliveryConfiguration)); + serviceCollection.AddSingleton(new EntriesApi(cloudContentDeliveryConfiguration)); + } + + static void RegisterAuthoringServices(IServiceCollection serviceCollection) + { + serviceCollection.AddSingleton(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + serviceCollection.AddScoped(); + + serviceCollection.AddScoped(); + serviceCollection.AddTransient(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationCreateHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationCreateHandler.cs new file mode 100644 index 0000000..7c6e96e --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationCreateHandler.cs @@ -0,0 +1,81 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class BuildConfigurationCreateHandler +{ + public static async Task BuildConfigurationCreateAsync( + BuildConfigurationCreateInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Creating build config...", _ => + BuildConfigurationCreateAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task BuildConfigurationCreateAsync( + BuildConfigurationCreateInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + + var binaryPath = input.BinaryPath ?? throw new MissingInputException(BuildConfigurationCreateInput.BinaryPathKey); + var buildId = input.BuildId ?? throw new MissingInputException(BuildConfigurationCreateInput.BuildIdKey); + var commandLine = input.CommandLine ?? throw new MissingInputException(BuildConfigurationCreateInput.CommandLineKey); + var configuration = input.Configuration ?? throw new MissingInputException(BuildConfigurationCreateInput.ConfigurationKey); + var cores = input.Cores ?? throw new MissingInputException(BuildConfigurationCreateInput.CoresKey); + var memory = input.Memory ?? throw new MissingInputException(BuildConfigurationCreateInput.MemoryKey); + var name = input.Name ?? throw new MissingInputException(BuildConfigurationCreateInput.NameKey); + var queryType = input.QueryType ?? throw new MissingInputException(BuildConfigurationCreateInput.QueryTypeKey); + var speed = input.Speed ?? throw new MissingInputException(BuildConfigurationCreateInput.SpeedKey); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var parsedConfigs = configuration.Select(ParseConfig).ToList(); + + var buildConfiguration = await service.BuildConfigurationsApi.CreateBuildConfigurationAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + new BuildConfigurationCreateRequest( + binaryPath: binaryPath, + buildID: buildId, + commandLine: commandLine, + configuration: parsedConfigs, + cores: cores, + memory: memory, + name: name, + queryType: queryType, + speed: speed + ), + cancellationToken: cancellationToken); + logger.LogResultValue(new BuildConfigurationOutput(buildConfiguration)); + } + + static ConfigurationPair ParseConfig(string rawKv) + { + var parts = rawKv.Split(':'); + if (parts.Length != 2) + { + throw new InvalidKeyValuePairException(rawKv); + } + + return new ConfigurationPair(key: parts[0], value: parts[1]); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationDeleteHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationDeleteHandler.cs new file mode 100644 index 0000000..499230a --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationDeleteHandler.cs @@ -0,0 +1,48 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Service; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class BuildConfigurationDeleteHandler +{ + public static async Task BuildConfigurationDeleteAsync( + BuildConfigurationIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Deleting build configuration...", _ => + BuildConfigurationDeleteAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task BuildConfigurationDeleteAsync( + BuildConfigurationIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + var buildConfigurationIdStr = input.BuildConfigurationId ?? throw new MissingInputException(BuildConfigurationIdInput.BuildConfigurationIdKey); + var buildConfigurationId = long.Parse(buildConfigurationIdStr); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + await service.BuildConfigurationsApi.DeleteBuildConfigurationAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + buildConfigurationId, + cancellationToken: cancellationToken); + + logger.LogInformation("Build configuration deleted successfully"); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationGetHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationGetHandler.cs new file mode 100644 index 0000000..afc46d4 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationGetHandler.cs @@ -0,0 +1,50 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class BuildConfigurationGetHandler +{ + public static async Task BuildConfigurationGetAsync( + BuildConfigurationIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Fetching build configuration...", _ => + BuildConfigurationGetAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task BuildConfigurationGetAsync( + BuildConfigurationIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + var buildConfigurationIdStr = input.BuildConfigurationId ?? throw new MissingInputException(BuildConfigurationIdInput.BuildConfigurationIdKey); + var buildConfigurationId = long.Parse(buildConfigurationIdStr); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var buildConfiguration = await service.BuildConfigurationsApi.GetBuildConfigurationAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + buildConfigurationId, + cancellationToken: cancellationToken); + + logger.LogResultValue(new BuildConfigurationOutput(buildConfiguration)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationListHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationListHandler.cs new file mode 100644 index 0000000..d0e024e --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationListHandler.cs @@ -0,0 +1,61 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class BuildConfigurationListHandler +{ + public static async Task BuildConfigurationListAsync( + BuildConfigurationListInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Fetching build configuration list...", _ => + BuildConfigurationListAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task BuildConfigurationListAsync( + BuildConfigurationListInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + + Guid? fleetId = null; + string? partial = null; + + if (input.FleetId != null) + { + fleetId = Guid.Parse(input.FleetId); + } + + if (input.Partial != null) + { + partial = input.Partial; + } + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var buildconfigs = await service.BuildConfigurationsApi.ListBuildConfigurationsAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + fleetId, + partial, + cancellationToken: cancellationToken); + + logger.LogResultValue(new BuildConfigurationListOutput(buildconfigs)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationUpdateHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationUpdateHandler.cs new file mode 100644 index 0000000..56a989a --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildConfigurationUpdateHandler.cs @@ -0,0 +1,105 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class BuildConfigurationUpdateHandler +{ + public static async Task BuildConfigurationUpdateAsync( + BuildConfigurationUpdateInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync( + "Updating build config...", + _ => + BuildConfigurationUpdateAsync( + input, + unityEnvironment, + service, + logger, + cancellationToken)); + } + + internal static async Task BuildConfigurationUpdateAsync( + BuildConfigurationUpdateInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var currentConfig = await service.BuildConfigurationsApi.GetBuildConfigurationAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + input.BuildConfigId, + cancellationToken: cancellationToken + ); + + List configurationParam; + + if (input.Configuration != null && input.Configuration?.Count != 0) + { + configurationParam = input.Configuration!.Select(ParseConfig).ToList(); + } + else + { + configurationParam = currentConfig._Configuration.Select(ConvertEntryToPair).ToList(); + } + + // API requires all these fields to be populated, to make it a nicer user experience we populate + // Null input values with the existing values + var req = new BuildConfigurationUpdateRequest( + binaryPath: input.BinaryPath ?? currentConfig.BinaryPath, + buildID: (input.BuildId is not null && input.BuildId != 0) ? input.BuildId.Value : currentConfig.BuildID, + commandLine: input.CommandLine ?? currentConfig.CommandLine, + configuration: configurationParam, + cores: input.Cores ?? currentConfig.Cores, + memory: input.Memory ?? currentConfig.Memory, + name: input.Name ?? currentConfig.Name, + queryType: input.QueryType ?? currentConfig.QueryType, + speed: input.Speed ?? currentConfig.Speed + ); + + var buildConfiguration = await service.BuildConfigurationsApi.UpdateBuildConfigurationAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + input.BuildConfigId, + req, + cancellationToken: cancellationToken); + + logger.LogResultValue(new BuildConfigurationOutput(buildConfiguration)); + } + + static ConfigurationPair1 ParseConfig(string rawKv) + { + var parts = rawKv.Split(':'); + if (parts.Length != 2) + { + throw new InvalidKeyValuePairException(rawKv); + } + + return new ConfigurationPair1(key: parts[0], value: parts[1]); + } + + static ConfigurationPair1 ConvertEntryToPair(ConfigEntry entry) + { + return new ConfigurationPair1(id: entry.Id, key: entry.Key, value: entry.Value); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateHandler.cs new file mode 100644 index 0000000..1eebb62 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateHandler.cs @@ -0,0 +1,52 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class BuildCreateHandler +{ + public static async Task BuildCreateAsync( + BuildCreateInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Creating build...", _ => + BuildCreateAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task BuildCreateAsync( + BuildCreateInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + var buildName = input.BuildName ?? throw new MissingInputException(BuildCreateInput.NameKey); + var buildOsFamily = input.BuildOsFamily ?? throw new MissingInputException(BuildCreateInput.OsFamilyKey); + var buildType = input.BuildType ?? throw new MissingInputException(BuildCreateInput.TypeKey); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var build = await service.BuildsApi.CreateBuildAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + new CreateBuildRequest(buildName, buildType, osFamily: buildOsFamily), + cancellationToken: cancellationToken); + + logger.LogResultValue(new BuildOutput(build)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionBucketHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionBucketHandler.cs new file mode 100644 index 0000000..febfef6 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionBucketHandler.cs @@ -0,0 +1,48 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static partial class BuildCreateVersionHandler +{ + static async Task CreateBucketUploadBuildVersion( + ILogger logger, + IGameServerHostingService service, + BuildCreateVersionInput input, + string environmentId, + CreateBuild200Response build, + CancellationToken cancellationToken + ) + { + ValidateBucketInput(input); + await service.BuildsApi.CreateNewBuildVersionAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + build.BuildID, + new CreateNewBuildVersionRequest( + s3: new AmazonS3Request(input.AccessKey!, input.BucketUrl!, input.SecretKey!)), + cancellationToken: cancellationToken + ); + + logger.LogInformation("Build version created successfully"); + } + + // We need to apply our own conditional validation based on the build type + internal static void ValidateBucketInput(BuildCreateVersionInput input) + { + if (input.AccessKey == null) + throw new MissingInputException(BuildCreateVersionInput.AccessKeyKey); + if (input.BucketUrl == null) + throw new MissingInputException(BuildCreateVersionInput.BucketUrlKey); + if (input.SecretKey == null) + throw new MissingInputException(BuildCreateVersionInput.SecretKeyKey); + if (input.ContainerTag != null) + throw new CliException("Build does not support container flags.", ExitCode.HandledError); + if (input.FileDirectory != null) + throw new CliException("Build does not support file upload flags.", ExitCode.HandledError); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionContainerHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionContainerHandler.cs new file mode 100644 index 0000000..c8ca314 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionContainerHandler.cs @@ -0,0 +1,42 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static partial class BuildCreateVersionHandler +{ + static async Task CreateContainerBuildVersion( + ILogger logger, + IGameServerHostingService service, + BuildCreateVersionInput input, + string environmentId, + CreateBuild200Response build, + CancellationToken cancellationToken + ) + { + ValidateContainerInput(input); + await service.BuildsApi.CreateNewBuildVersionAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + build.BuildID, + new CreateNewBuildVersionRequest(container: new ContainerImage(input.ContainerTag!)), + cancellationToken: cancellationToken + ); + logger.LogInformation("Build version created successfully"); + } + + // We need to apply our own conditional validation based on the build type + internal static void ValidateContainerInput(BuildCreateVersionInput input) + { + if (input.ContainerTag == null) + throw new MissingInputException(BuildCreateVersionInput.ContainerTagKey); + if (input.FileDirectory != null) + throw new CliException("Build does not support file upload flags.", ExitCode.HandledError); + if (input.BucketUrl != null) + throw new CliException("Build does not support s3 buckets.", ExitCode.HandledError); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionFileUploadHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionFileUploadHandler.cs new file mode 100644 index 0000000..604b2c4 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionFileUploadHandler.cs @@ -0,0 +1,280 @@ +using System.Net; +using System.Text; +using Microsoft.Extensions.Logging; +using Polly; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Client; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static partial class BuildCreateVersionHandler +{ + static readonly List k_FilesToIgnore = new() + { + ".DS_Store" + }; + + static int Limit => 100; + + static async Task CreateFileUploadBuildVersion( + ILogger logger, + IGameServerHostingService service, + BuildCreateVersionInput input, + string environmentId, + CreateBuild200Response build, + HttpClient httpClient, + CancellationToken cancellationToken + ) + { + if (build.SyncStatus == CreateBuild200Response.SyncStatusEnum.SYNCING) + throw new CliException("Build is currently syncing. Please try again soon....", ExitCode.HandledError); + + var localFiles = GetLocalFiles(input.FileDirectory!, logger); + + if (!localFiles.Any()) + { + logger.LogInformation("No files to upload"); + return; + } + + var uploaded = await UploadFiles( + service, + input.CloudProjectId!, + environmentId, + build, + localFiles, + httpClient, + cancellationToken); + + if (uploaded == 0) + { + logger.LogInformation("No files uploaded"); + return; + } + + var deleted = 0; + if (input.RemoveOldFiles ?? false) + deleted = await DeleteOldFiles( + service, + input.CloudProjectId!, + environmentId, + build, + localFiles, + cancellationToken); + + var policy = Policy + .Handle() + .WaitAndRetryAsync(6, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); + + await policy.ExecuteAsync( + async () => await service.BuildsApi.CreateNewBuildVersionAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + build.BuildID, + new CreateNewBuildVersionRequest(new CCDDetails2(build.Ccd.BucketID)), + cancellationToken: cancellationToken + )); + + var details = new StringBuilder() + .AppendLine($"Files to upload: {localFiles.Count}") + .AppendLine($"Files uploaded: {uploaded}") + .AppendLine($"Files deleted: {deleted}") + .ToString(); + + logger.LogInformation("Build version created successfully\n{}", details); + } + + static List GetLocalFiles(string directory, ILogger logger) + { + try + { + var localFiles = Directory.GetFiles(directory, "*", SearchOption.AllDirectories) + .Where(fullSystemPathFile => !k_FilesToIgnore.Any(fullSystemPathFile.Contains)) + .Select( + f => + { + var pathWithInDir = f.Replace(directory, ""); + + // handle windows file system + pathWithInDir = pathWithInDir.Replace("\\", "/"); + + if (pathWithInDir[0] == '/') pathWithInDir = pathWithInDir.Substring(1); + + return new LocalFile(f, pathWithInDir); + }) + .ToList(); + + return localFiles; + } + catch (DirectoryNotFoundException) + { + logger.LogError("directory {} could not be found", directory); + + return new List(); + } + } + + static async Task> GetRemoteFiles( + IGameServerHostingService service, + string projectId, + string environmentId, + CreateBuild200Response build, + CancellationToken cancellationToken + ) + { + var offset = 0; + var remoteFiles = new List(); + while (true) + { + var buildsFileList = await GetBuildsFileList( + service, + projectId, + environmentId, + build.BuildID, + Limit, + offset, + cancellationToken + ); + + if (buildsFileList.Results.Count == 0) + break; + + remoteFiles.AddRange(buildsFileList.Results); + + if (buildsFileList.Results.Count < Limit) + break; + + offset = buildsFileList.Offset + buildsFileList.Results.Count; + } + + return remoteFiles; + } + + static async Task UploadFiles( + IGameServerHostingService service, + string projectId, + string environmentId, + CreateBuild200Response build, + List localFiles, + HttpClient httpClient, + CancellationToken cancellationToken + ) + { + var uploaded = 0; + foreach (var fileToUpload in localFiles) + { + var localFile = File.OpenRead(fileToUpload.GetSystemPath()); + var remoteFile = await service.BuildsApi.CreateOrUpdateBuildFileAsync( + Guid.Parse(projectId), + Guid.Parse(environmentId), + build.BuildID, + new CreateOrUpdateBuildFileRequest(fileToUpload.GetPathInDirectory()), + cancellationToken: cancellationToken + ); + + if (remoteFile.Uploaded) + continue; + + if (remoteFile.SignedUrl == "") + throw new InvalidResponseException( + $"signedUrl missing from entry with path {fileToUpload.GetPathInDirectory()} response"); + + + await httpClient.PutAsync(remoteFile.SignedUrl, new StreamContent(localFile), cancellationToken); + uploaded++; + } + + return uploaded; + } + + static async Task DeleteOldFiles( + IGameServerHostingService service, + string projectId, + string environmentId, + CreateBuild200Response build, + IReadOnlyCollection localFiles, + CancellationToken cancellationToken + ) + { + var remoteFiles = await GetRemoteFiles( + service, + projectId, + environmentId, + build, + cancellationToken); + + var filesToDelete = remoteFiles + .Where(LocalFilesDoesNotContainEntry(localFiles)); + + var deleted = 0; + foreach (var f in filesToDelete) + { + await service.BuildsApi.DeleteBuildFileByPathAsync( + Guid.Parse(projectId), + Guid.Parse(environmentId), + build.BuildID, + f.Path, + cancellationToken: cancellationToken + ); + deleted++; + } + + return deleted; + } + + static async Task GetBuildsFileList( + IGameServerHostingService service, + string projectId, + string environmentId, + long buildId, + int limit, + int offset, + CancellationToken cancellationToken + ) + { + try + { + var buildsFileListResponse = await service.BuildsApi.GetBuildFilesAsync( + Guid.Parse(projectId), + Guid.Parse(environmentId), + buildId, + limit, + offset, + cancellationToken: cancellationToken + ); + + return buildsFileListResponse; + } + catch (ApiException e) + { + if ((HttpStatusCode)e.ErrorCode != HttpStatusCode.Conflict) + throw; + + // return empty list + return new BuildFilesList(limit, offset, new List()); + } + } + + static Func LocalFilesDoesNotContainEntry( + IReadOnlyCollection localFiles + ) + { + return remoteEntry => localFiles.All(localFile => localFile.GetPathInDirectory() != remoteEntry.Path); + } + + // We need to apply our own conditional validation based on the build type + internal static void ValidateFileUploadInput(BuildCreateVersionInput input) + { + if (input.FileDirectory == null) + throw new MissingInputException(BuildCreateVersionInput.FileDirectoryKey); + if (input.ContainerTag != null) + throw new CliException("Build does not support container flags.", ExitCode.HandledError); + if (input.BucketUrl != null) + throw new CliException("Build does not support s3 buckets.", ExitCode.HandledError); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionHandler.cs new file mode 100644 index 0000000..4603389 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildCreateVersionHandler.cs @@ -0,0 +1,94 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Service; +using static Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model.CreateBuild200Response.BuildTypeEnum; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static partial class BuildCreateVersionHandler +{ + public static async Task BuildCreateVersionAsync( + BuildCreateVersionInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + HttpClient httpClient, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync( + "Creating build version...", + _ => BuildCreateVersionAsync( + input, + unityEnvironment, + service, + logger, + httpClient, + cancellationToken: cancellationToken + )); + } + + internal static async Task BuildCreateVersionAsync( + BuildCreateVersionInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + HttpClient httpClient, + CancellationToken cancellationToken = default + ) + { + httpClient.Timeout = TimeSpan.FromMinutes(10); + + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + var buildIdStr = input.BuildId ?? throw new MissingInputException(BuildCreateVersionInput.BuildIdKey); + var buildId = long.Parse(buildIdStr); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var build = await service.BuildsApi.GetBuildAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + buildId, + cancellationToken: cancellationToken); + + switch (build.BuildType) + { + case CONTAINER: + await CreateContainerBuildVersion( + logger, + service, + input, + environmentId, + build, + cancellationToken); + break; + case FILEUPLOAD: + await CreateFileUploadBuildVersion( + logger, + service, + input, + environmentId, + build, + httpClient, + cancellationToken + ); + break; + case S3: + await CreateBucketUploadBuildVersion( + logger, + service, + input, + environmentId, + build, + cancellationToken); + break; + default: + throw new CliException("Unsupported build type", ExitCode.HandledError); + } + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildDeleteHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildDeleteHandler.cs new file mode 100644 index 0000000..425bcd1 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildDeleteHandler.cs @@ -0,0 +1,48 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Service; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class BuildDeleteHandler +{ + public static async Task BuildDeleteAsync( + BuildIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Deleting build...", _ => + BuildDeleteAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task BuildDeleteAsync( + BuildIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + var buildIdStr = input.BuildId ?? throw new MissingInputException(BuildIdInput.BuildIdKey); + var buildId = long.Parse(buildIdStr); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + await service.BuildsApi.DeleteBuildAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + buildId, + cancellationToken: cancellationToken); + + logger.LogInformation("Build deleted successfully"); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildGetHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildGetHandler.cs new file mode 100644 index 0000000..68f02f8 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildGetHandler.cs @@ -0,0 +1,50 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class BuildGetHandler +{ + public static async Task BuildGetAsync( + BuildIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Fetching build...", _ => + BuildGetAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task BuildGetAsync( + BuildIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + var buildIdStr = input.BuildId ?? throw new MissingInputException(BuildIdInput.BuildIdKey); + var buildId = long.Parse(buildIdStr); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var build = await service.BuildsApi.GetBuildAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + buildId, + cancellationToken: cancellationToken); + + logger.LogResultValue(new BuildOutput(build)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildInstallsHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildInstallsHandler.cs new file mode 100644 index 0000000..25750ee --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildInstallsHandler.cs @@ -0,0 +1,50 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class BuildInstallsHandler +{ + public static async Task BuildInstallsAsync( + BuildIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Fetching installs...", _ => + BuildInstallsAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task BuildInstallsAsync( + BuildIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + var buildIdStr = input.BuildId ?? throw new MissingInputException(BuildIdInput.BuildIdKey); + var buildId = long.Parse(buildIdStr); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var build = await service.BuildsApi.GetBuildInstallsAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + buildId, + cancellationToken: cancellationToken); + + logger.LogResultValue(new BuildInstallsOutput(build)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildListHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildListHandler.cs new file mode 100644 index 0000000..13ac221 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildListHandler.cs @@ -0,0 +1,46 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Input; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class BuildListHandler +{ + public static async Task BuildListAsync( + CommonInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Fetching build list...", _ => + BuildListAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task BuildListAsync( + CommonInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var builds = await service.BuildsApi.ListBuildsAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + cancellationToken: cancellationToken); + + logger.LogResultValue(new BuildListOutput(builds)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildUpdateHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildUpdateHandler.cs new file mode 100644 index 0000000..34652d4 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/BuildUpdateHandler.cs @@ -0,0 +1,50 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class BuildUpdateHandler +{ + public static async Task BuildUpdateAsync( + BuildUpdateInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Updating build...", _ => + BuildUpdateAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task BuildUpdateAsync( + BuildUpdateInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + var buildIdStr = input.BuildId ?? throw new MissingInputException(BuildIdInput.BuildIdKey); + var buildId = long.Parse(buildIdStr); + var newBuildName = input.BuildName ?? throw new MissingInputException(BuildUpdateInput.NameKey); + + await service.AuthorizeGameServerHostingService(cancellationToken); + await service.BuildsApi.UpdateBuildAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + buildId, + new UpdateBuildRequest(newBuildName), + cancellationToken: cancellationToken); + + logger.LogInformation("Build updated successfully"); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetCreateHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetCreateHandler.cs new file mode 100644 index 0000000..12ac624 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetCreateHandler.cs @@ -0,0 +1,63 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class FleetCreateHandler +{ + public static async Task FleetCreateAsync(FleetCreateInput input, IUnityEnvironment unityEnvironment, + IGameServerHostingService service, ILogger logger, ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken) + { + await loadingIndicator.StartLoadingAsync($"Creating fleet...", + _ => FleetCreateAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task FleetCreateAsync(FleetCreateInput input, IUnityEnvironment unityEnvironment, + IGameServerHostingService service, ILogger logger, CancellationToken cancellationToken) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + var fleetName = input.FleetName ?? throw new MissingInputException(FleetCreateInput.NameKey); + var osFamily = input.OsFamily ?? throw new MissingInputException(FleetCreateInput.OsFamilyKey); + var regions = input.Regions ?? throw new MissingInputException(FleetCreateInput.RegionsKey); + var buildConfigurations = input.BuildConfigurations ?? throw new MissingInputException(FleetCreateInput.BuildConfigurationsKey); + + if (buildConfigurations.Length == 0) + { + throw new MissingInputException(FleetCreateInput.BuildConfigurationsKey); + } + + if (regions.Length == 0) + { + throw new MissingInputException(FleetCreateInput.RegionsKey); + } + + await service.AuthorizeGameServerHostingService(cancellationToken); + + // Parse pre-validated regions, and convert to a region list. + var regionIdList = regions.Select(Guid.Parse).ToList(); + var regionCreateRequestList = regionIdList.Select(regionId => new Region(regionID: regionId)).ToList(); + + var fleet = await service.FleetsApi.CreateFleetAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + new FleetCreateRequest( + name: fleetName, + osFamily: osFamily, + regions: regionCreateRequestList, + buildConfigurations: buildConfigurations.ToList() + ), + cancellationToken: cancellationToken + ); + + logger.LogResultValue(new FleetGetOutput(fleet)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetDeleteHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetDeleteHandler.cs new file mode 100644 index 0000000..15375f9 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetDeleteHandler.cs @@ -0,0 +1,48 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Service; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class FleetDeleteHandler +{ + public static async Task FleetDeleteAsync( + FleetIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Deleting fleet...", + _ => FleetDeleteAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task FleetDeleteAsync( + FleetIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + var fleetId = input.FleetId ?? throw new MissingInputException(FleetIdInput.FleetIdKey); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + await service.FleetsApi.DeleteFleetAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + Guid.Parse(fleetId), + cancellationToken: cancellationToken + ); + + logger.LogInformation("Fleet deleted successfully"); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetGetHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetGetHandler.cs new file mode 100644 index 0000000..66583df --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetGetHandler.cs @@ -0,0 +1,49 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class FleetGetHandler +{ + public static async Task FleetGetAsync( + FleetIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Fetching fleet...", + _ => FleetGetAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task FleetGetAsync( + FleetIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + var fleetId = input.FleetId ?? throw new MissingInputException(FleetIdInput.FleetIdKey); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var fleet = await service.FleetsApi.GetFleetAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + Guid.Parse(fleetId), + cancellationToken: cancellationToken); + + logger.LogResultValue(new FleetGetOutput(fleet)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetListHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetListHandler.cs new file mode 100644 index 0000000..c5e03aa --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetListHandler.cs @@ -0,0 +1,46 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Input; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class FleetListHandler +{ + public static async Task FleetListAsync( + CommonInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Fetching fleet list...", + _ => FleetListAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task FleetListAsync( + CommonInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var fleets = await service.FleetsApi.ListFleetsAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + cancellationToken: cancellationToken); + + logger.LogResultValue(new FleetListOutput(fleets)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetRegionCreateHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetRegionCreateHandler.cs new file mode 100644 index 0000000..4b53773 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetRegionCreateHandler.cs @@ -0,0 +1,59 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class FleetRegionCreateHandler +{ + public static async Task FleetRegionCreateAsync( + FleetRegionCreateInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Creating fleet region...", + _ => FleetRegionCreateAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task FleetRegionCreateAsync( + FleetRegionCreateInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + var fleetId = input.FleetId ?? throw new MissingInputException(FleetRegionCreateInput.FleetIdKey); + var regionId = input.RegionId ?? throw new MissingInputException(FleetRegionCreateInput.RegionIdKey); + var maxServers = input.MaxServers ?? throw new MissingInputException(FleetRegionCreateInput.MaxServersKey); + var minAvailableServers = input.MinAvailableServers ?? throw new MissingInputException(FleetRegionCreateInput.MinAvailableServersKey); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var addFleetRegionsResponse = await service.FleetsApi.AddFleetRegionAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + fleetId, + new AddRegionRequest( + maxServers, + minAvailableServers, + regionId + ), + cancellationToken: cancellationToken + ); + + logger.LogResultValue(new FleetRegionCreateOutput(addFleetRegionsResponse)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetRegionUpdateHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetRegionUpdateHandler.cs new file mode 100644 index 0000000..001681f --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetRegionUpdateHandler.cs @@ -0,0 +1,67 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class FleetRegionUpdateHandler +{ + public static async Task FleetRegionUpdateAsync( + FleetRegionUpdateInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync($"Updating fleet region...", + _ => FleetRegionUpdateAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task FleetRegionUpdateAsync( + FleetRegionUpdateInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + var fleetId = input.FleetId ?? throw new MissingInputException(FleetRegionUpdateInput.FleetIdKey); + var regionId = input.RegionId ?? throw new MissingInputException(FleetRegionUpdateInput.RegionIdKey); + var deleteTtl = input.DeleteTtl ?? throw new MissingInputException(FleetRegionUpdateInput.DeleteTtlKey); + var disabledDeleteTtl = input.DisabledDeleteTtl ?? throw new MissingInputException(FleetRegionUpdateInput.DisabledDeleteTtlKey); + var maxServers = input.MaxServers ?? throw new MissingInputException(FleetRegionUpdateInput.MaxServersKey); + var minAvailableServers = input.MinAvailableServers ?? throw new MissingInputException(FleetRegionUpdateInput.MinAvailableServersKey); + var scalingEnabled = input.ScalingEnabled ?? throw new MissingInputException(FleetRegionUpdateInput.ScalingEnabledKey); + var shutdownTtl = input.ShutdownTtl ?? throw new MissingInputException(FleetRegionUpdateInput.ShutdownTtlKey); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var updateFleetRegionResponse = await service.FleetsApi.UpdateFleetRegionAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + fleetId, + regionId, + new UpdateRegionRequest( + deleteTtl, + disabledDeleteTtl, + maxServers, + minAvailableServers, + scalingEnabled, + shutdownTtl + ), + cancellationToken: cancellationToken + ); + + logger.LogResultValue(new FleetRegionUpdateOutput(updateFleetRegionResponse)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetUpdateHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetUpdateHandler.cs new file mode 100644 index 0000000..10e5b47 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/FleetUpdateHandler.cs @@ -0,0 +1,67 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Service; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class FleetUpdateHandler +{ + public static async Task FleetUpdateAsync(FleetUpdateInput input, IUnityEnvironment unityEnvironment, + IGameServerHostingService service, ILogger logger, ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken) + { + await loadingIndicator.StartLoadingAsync($"Updating fleet...", + _ => FleetUpdateAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task FleetUpdateAsync(FleetUpdateInput input, IUnityEnvironment unityEnvironment, + IGameServerHostingService service, ILogger logger, CancellationToken cancellationToken) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + + var fleetId = input.FleetId ?? throw new MissingInputException(FleetIdInput.FleetIdKey); + var allocTtl = input.AllocTtl ?? throw new InvalidCastException(); + var deleteTtl = input.DeleteTtl ?? throw new InvalidCastException(); + var disabledDeleteTtl = input.DisabledDeleteTtl ?? throw new InvalidCastException(); + var shutdownTtl = input.ShutdownTtl ?? throw new InvalidCastException(); + var buildConfigs = input.BuildConfigs; + var name = input.Name; + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var fleet = await service.FleetsApi.GetFleetAsync(Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), Guid.Parse(fleetId), cancellationToken: cancellationToken); + + // Build Configs and Name are required parameters. We populate the update request with the previous values + // If the user does not provide these flags. + if (buildConfigs?.Count is 0 || name is null) + { + name ??= fleet.Name; + + if (buildConfigs?.Count is 0) + { + buildConfigs = fleet.BuildConfigurations.ConvertAll(b => b.Id); + } + } + + FleetUpdateRequest fleetUpdateReq = new(name: name, buildConfigurations: buildConfigs!, allocationTTL: allocTtl, + deleteTTL: deleteTtl, disabledDeleteTTL: disabledDeleteTtl, shutdownTTL: shutdownTtl) + { + // The API docs for the OsID field mark it as a required, but deprecated field + // We have a ticket (MPA-1734) to resolve this in future. +#pragma warning disable 612 + OsID = fleet.OsID +#pragma warning restore 612 + }; + + await service.FleetsApi.UpdateFleetAsync(Guid.Parse(input.CloudProjectId!), Guid.Parse(environmentId), + Guid.Parse(fleetId), fleetUpdateReq, cancellationToken: cancellationToken); + + logger.LogInformation("Fleet updated successfully"); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/RegionAvailableHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/RegionAvailableHandler.cs new file mode 100644 index 0000000..af6f1be --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/RegionAvailableHandler.cs @@ -0,0 +1,50 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class RegionAvailableHandler +{ + public static async Task RegionAvailableAsync( + FleetIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Fetching available regions...", _ => + RegionAvailableAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task RegionAvailableAsync( + FleetIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + var fleetId = input.FleetId ?? throw new MissingInputException(FleetIdInput.FleetIdKey); + + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var builds = await service.FleetsApi.GetAvailableFleetRegionsAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + Guid.Parse(fleetId), + cancellationToken: cancellationToken); + + logger.LogResultValue(new RegionTemplateListOutput(builds)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/RegionTemplatesHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/RegionTemplatesHandler.cs new file mode 100644 index 0000000..181eb79 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/RegionTemplatesHandler.cs @@ -0,0 +1,46 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Input; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class RegionTemplatesHandler +{ + public static async Task RegionTemplatesAsync( + CommonInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Fetching region list...", + _ => RegionTemplatesAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task RegionTemplatesAsync( + CommonInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var regions = await service.FleetsApi.ListTemplateFleetRegionsAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + cancellationToken: cancellationToken); + + logger.LogResultValue(new RegionTemplateListOutput(regions)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/ServerGetHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/ServerGetHandler.cs new file mode 100644 index 0000000..26e737a --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/ServerGetHandler.cs @@ -0,0 +1,49 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class ServerGetHandler +{ + public static async Task ServerGetAsync( + ServerIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Fetching server...", + _ => ServerGetAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task ServerGetAsync( + ServerIdInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + var serverId = input.ServerId ?? throw new MissingInputException(ServerIdInput.ServerIdKey); + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var server = await service.ServersApi.GetServerAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + long.Parse(serverId), + cancellationToken: cancellationToken); + + logger.LogResultValue(new ServerGetOutput(server)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/ServerListHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/ServerListHandler.cs new file mode 100644 index 0000000..3f7bc24 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Handlers/ServerListHandler.cs @@ -0,0 +1,76 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.Common.Console; +using Unity.Services.Cli.Common.Logging; +using Unity.Services.Cli.Common.Utils; +using Unity.Services.Cli.GameServerHosting.Input; +using Unity.Services.Cli.GameServerHosting.Model; +using Unity.Services.Cli.GameServerHosting.Service; + +namespace Unity.Services.Cli.GameServerHosting.Handlers; + +static class ServerListHandler +{ + public static async Task ServerListAsync( + ServerListInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + ILoadingIndicator loadingIndicator, + CancellationToken cancellationToken + ) + { + await loadingIndicator.StartLoadingAsync("Fetching server list...", + _ => ServerListAsync(input, unityEnvironment, service, logger, cancellationToken)); + } + + internal static async Task ServerListAsync( + ServerListInput input, + IUnityEnvironment unityEnvironment, + IGameServerHostingService service, + ILogger logger, + CancellationToken cancellationToken + ) + { + // FetchIdentifierAsync handles null checks for project-id and environment + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); + + Guid? fleetId = null; + string? buildConfigurationId = null; + string? partial = null; + string? status = null; + + if (input.FleetId != null) + { + fleetId = Guid.Parse(input.FleetId); + } + + if (input.BuildConfigurationId != null) + { + buildConfigurationId = input.BuildConfigurationId; + } + + if (input.Partial != null) + { + partial = input.Partial; + } + + if (input.Status != null) + { + status = input.Status; + } + + await service.AuthorizeGameServerHostingService(cancellationToken); + + var servers = await service.ServersApi.ListServersAsync( + Guid.Parse(input.CloudProjectId!), + Guid.Parse(environmentId), + fleetId: fleetId, + buildConfigurationId: buildConfigurationId, + partial: partial, + status: status, + cancellationToken: cancellationToken + ); + + logger.LogResultValue(new ServersOutput(servers)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationCreateInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationCreateInput.cs new file mode 100644 index 0000000..0b4f857 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationCreateInput.cs @@ -0,0 +1,94 @@ +using System.CommandLine; +using Unity.Services.Cli.Common.Input; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +public class BuildConfigurationCreateInput : CommonInput +{ + + public const string BinaryPathKey = "--binary-path"; + public const string BuildIdKey = "--build"; + public const string CommandLineKey = "--command-line"; + public const string ConfigurationKey = "--configuration"; + public const string CoresKey = "--cores"; + public const string MemoryKey = "--memory"; + public const string NameKey = "--name"; + public const string QueryTypeKey = "--query-type"; + public const string SpeedKey = "--speed"; + + + public static readonly Option BinaryPathOption = new(BinaryPathKey, "Path to the game binary") + { + IsRequired = true + }; + + public static readonly Option BuildIdOption = new(BuildIdKey, "Build to associate with the new build configuration") + { + IsRequired = true + }; + + public static readonly Option CommandLineOption = new(CommandLineKey, "Binary launch parameters") + { + IsRequired = true + }; + + public static readonly Option> ConfigurationOption = new( + ConfigurationKey, + "List of \"key:value\" pairs used to configure this build configuration, supports multiple values") + { + AllowMultipleArgumentsPerToken = true + }; + + public static readonly Option CoresOption = new(CoresKey, "The number of CPU cores required per server") + { + IsRequired = true + }; + + public static readonly Option MemoryOption = new(MemoryKey, "Maximum memory required per server (MB)") + { + IsRequired = true + }; + + public static readonly Option NameOption = new(NameKey, "Name to use for the new build configuration") + { + IsRequired = true + }; + + public static readonly Option QueryTypeOption = new(QueryTypeKey, "Query type supported by this build configuration") + { + IsRequired = true + }; + + public static readonly Option SpeedOption = new(SpeedKey, "CPU utilisation per core") + { + IsRequired = true + }; + + [InputBinding(nameof(BinaryPathOption))] + public string? BinaryPath { get; init; } + + [InputBinding(nameof(BuildIdOption))] + public long? BuildId { get; init; } + + [InputBinding(nameof(CommandLineOption))] + public string? CommandLine { get; init; } + + [InputBinding(nameof(ConfigurationOption))] + public List? Configuration { get; init; } + + [InputBinding(nameof(CoresOption))] + public long? Cores { get; init; } + + [InputBinding(nameof(MemoryOption))] + public long? Memory { get; init; } + + [InputBinding(nameof(NameOption))] + public string? Name { get; init; } + + [InputBinding(nameof(QueryTypeOption))] + public string? QueryType { get; init; } + + [InputBinding(nameof(SpeedOption))] + public long? Speed { get; init; } + +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationIdInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationIdInput.cs new file mode 100644 index 0000000..b2fce86 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationIdInput.cs @@ -0,0 +1,33 @@ +using System.CommandLine; +using Unity.Services.Cli.Common.Input; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +public class BuildConfigurationIdInput : CommonInput +{ + public const string BuildConfigurationIdKey = "build-configuration-id"; + + public static readonly Argument BuildConfigurationIdArgument = new( + BuildConfigurationIdKey, + "The unique ID of the build configuration" + ); + + [InputBinding(nameof(BuildConfigurationIdArgument))] + public string? BuildConfigurationId { get; init; } + + static BuildConfigurationIdInput() + { + BuildConfigurationIdArgument.AddValidator(result => + { + var value = result.GetValueOrDefault(); + try + { + _ = long.Parse(value); + } + catch (Exception) + { + result.ErrorMessage = $"Build Configuration ID '{value}' not a valid ID."; + } + }); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationListInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationListInput.cs new file mode 100644 index 0000000..5f2c680 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationListInput.cs @@ -0,0 +1,47 @@ +using System.CommandLine; +using System.CommandLine.Parsing; +using Unity.Services.Cli.Common.Input; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +public class BuildConfigurationListInput : CommonInput +{ + public const string FleetIdKey = "--fleet-id"; + public const string PartialKey = "--partial"; + + public static readonly Option FleetIdOption = new( + FleetIdKey, + "The fleet ID to filter the build configuration list by." + ); + + public static readonly Option PartialOption = new( + PartialKey, + "The partial search to filter the build configuration list by." + ); + + static BuildConfigurationListInput() + { + FleetIdOption.AddValidator(ValidateFleetId); + } + + [InputBinding(nameof(FleetIdOption))] + public string? FleetId { get; set; } + + + [InputBinding(nameof(PartialOption))] + public string? Partial { get; set; } + + static void ValidateFleetId(OptionResult result) + { + var value = result.GetValueOrDefault(); + if (value == null) return; + try + { + Guid.Parse(value); + } + catch (Exception) + { + result.ErrorMessage = $"Invalid option for --fleet-id. {value} is not a valid UUID"; + } + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationUpdateInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationUpdateInput.cs new file mode 100644 index 0000000..4931837 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildConfigurationUpdateInput.cs @@ -0,0 +1,14 @@ +using System.CommandLine; +using Unity.Services.Cli.Common.Input; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +public class BuildConfigurationUpdateInput : BuildConfigurationCreateInput +{ + public const string BuildConfigIdKey = "build-configuration-id"; + + public static readonly Argument BuildConfigIdArgument = new(BuildConfigIdKey, "The ID of the build configuration to update"); + + [InputBinding(nameof(BuildConfigIdArgument))] + public long BuildConfigId { get; init; } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildCreateInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildCreateInput.cs new file mode 100644 index 0000000..06a8c77 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildCreateInput.cs @@ -0,0 +1,67 @@ +using System.CommandLine; +using System.CommandLine.Parsing; +using Unity.Services.Cli.Common.Input; +using static Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model.CreateBuildRequest; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +class BuildCreateInput : CommonInput +{ + public const string NameKey = "--name"; + public const string OsFamilyKey = "--os-family"; + public const string TypeKey = "--type"; + + public static readonly Option BuildNameOption = new(NameKey, "The name of the build") + { + IsRequired = true + }; + + public static readonly Option BuildOsFamilyOption = new(OsFamilyKey, "The OS of the build") + { + IsRequired = true + }; + + public static readonly Option BuildTypeOption = new(TypeKey, "The type of the build") + { + IsRequired = true + }; + + static BuildCreateInput() + { + BuildOsFamilyOption.AddValidator(ValidateOsFamilyEnum); + BuildTypeOption.AddValidator(ValidateBuildTypeEnum); + } + + [InputBinding(nameof(BuildNameOption))] + public string? BuildName { get; init; } + + [InputBinding(nameof(BuildOsFamilyOption))] + public OsFamilyEnum? BuildOsFamily { get; init; } + + [InputBinding(nameof(BuildTypeOption))] + public BuildTypeEnum? BuildType { get; init; } + + static void ValidateOsFamilyEnum(OptionResult result) + { + try + { + result.GetValueOrDefault(); + } + catch (Exception) + { + result.ErrorMessage = $"Invalid option for --os-family. Did you mean one of the following? {string.Join(", ", Enum.GetNames())}"; + } + } + + static void ValidateBuildTypeEnum(OptionResult result) + { + try + { + result.GetValueOrDefault(); + } + catch (Exception) + { + result.ErrorMessage = $"Invalid option for --type. Did you mean one of the following? {string.Join(", ", Enum.GetNames())}"; + } + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildCreateVersionInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildCreateVersionInput.cs new file mode 100644 index 0000000..36dedb0 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildCreateVersionInput.cs @@ -0,0 +1,64 @@ +using System.CommandLine; +using Unity.Services.Cli.Common.Input; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +class BuildCreateVersionInput : CommonInput +{ + public const string AccessKeyKey = "--access-key"; + public const string BuildIdKey = "build-id"; + public const string BucketUrlKey = "--bucket-url"; + public const string ContainerTagKey = "--tag"; + public const string FileDirectoryKey = "--directory"; + public const string RemoveOldFilesKey = "--remove-old-files"; + public const string SecretKeyKey = "--secret-key"; + + public static readonly Option AccessKeyOption = new( + AccessKeyKey, + "The Amazon Web Services (AWS) access key, for s3 bucket builds"); + + public static readonly Argument BuildIdArgument = new( + BuildIdKey, + "The ID of the build to create a new version for"); + + public static readonly Option BucketUrlOption = new( + BucketUrlKey, + "The bucket url to use for the build version, for s3 bucket builds"); + + public static readonly Option ContainerTagOption = new( + ContainerTagKey, + "The container image tag to use for the build version, for container builds"); + + public static readonly Option FileDirectoryOption = new( + FileDirectoryKey, + "The directory of files to upload to the build version, for file upload builds"); + + public static readonly Option RemoveOldFilesOption = new( + RemoveOldFilesKey, + "Whether to remove old files from the build version, for file upload builds"); + + public static readonly Option SecretKeyOption = new( + SecretKeyKey, + "The Amazon Web Services (AWS) secret key, for s3 bucket builds"); + + [InputBinding(nameof(AccessKeyOption))] + public string? AccessKey { get; init; } + + [InputBinding(nameof(BuildIdArgument))] + public string? BuildId { get; init; } + + [InputBinding(nameof(BucketUrlOption))] + public string? BucketUrl { get; init; } + + [InputBinding(nameof(ContainerTagOption))] + public string? ContainerTag { get; init; } + + [InputBinding(nameof(FileDirectoryOption))] + public string? FileDirectory { get; init; } + + [InputBinding(nameof(RemoveOldFilesOption))] + public bool? RemoveOldFiles { get; init; } + + [InputBinding(nameof(SecretKeyOption))] + public string? SecretKey { get; init; } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildIdInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildIdInput.cs new file mode 100644 index 0000000..5b2aa67 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildIdInput.cs @@ -0,0 +1,30 @@ +using System.CommandLine; +using Unity.Services.Cli.Common.Input; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +class BuildIdInput : CommonInput +{ + public const string BuildIdKey = "build-id"; + + public static readonly Argument BuildIdArgument = new(BuildIdKey, "The unique ID of the build"); + + static BuildIdInput() + { + BuildIdArgument.AddValidator(result => + { + var value = result.GetValueOrDefault(); + try + { + _ = long.Parse(value); + } + catch (Exception) + { + result.ErrorMessage = $"Build ID '{value}' not a valid ID."; + } + }); + } + + [InputBinding(nameof(BuildIdArgument))] + public string? BuildId { get; init; } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildUpdateInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildUpdateInput.cs new file mode 100644 index 0000000..9b44346 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/BuildUpdateInput.cs @@ -0,0 +1,17 @@ +using System.CommandLine; +using Unity.Services.Cli.Common.Input; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +class BuildUpdateInput : BuildIdInput +{ + public const string NameKey = "--name"; + + public static readonly Option BuildNameOption = new(NameKey, "The name of the build") + { + IsRequired = true + }; + + [InputBinding(nameof(BuildNameOption))] + public string? BuildName { get; init; } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetCreateInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetCreateInput.cs new file mode 100644 index 0000000..47a2f91 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetCreateInput.cs @@ -0,0 +1,94 @@ +using System.CommandLine; +using System.CommandLine.Parsing; +using Unity.Services.Cli.Common.Input; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +public class FleetCreateInput : CommonInput +{ + public const string NameKey = "--name"; + public const string OsFamilyKey = "--os-family"; + public const string RegionsKey = "--region-id"; + public const string BuildConfigurationsKey = "--build-configuration-id"; + + public static readonly Option FleetNameOption = new( + NameKey, + "The name of the fleet." + ) + { + IsRequired = true + }; + + public static readonly Option FleetOsFamilyOption = new( + OsFamilyKey, + "The OS family that the build is based on. Only LINUX currently supported." + ) + { + IsRequired = true + }; + + public static readonly Option FleetRegionsOption = new( + RegionsKey, + "Template fleet region ID to be added to this fleet. Can be supplied more than once." + ) + { + AllowMultipleArgumentsPerToken = true, + IsRequired = true + }; + + public static readonly Option FleetBuildConfigurationsOption = new( + BuildConfigurationsKey, + "Build configuration to be added to the fleet. Can be supplied more than once." + ) + { + AllowMultipleArgumentsPerToken = true, + IsRequired = true + }; + + static FleetCreateInput() + { + FleetRegionsOption.AddValidator(ValidateRegionIds); + FleetOsFamilyOption.AddValidator(ValidateOsFamilyEnum); + } + + [InputBinding(nameof(FleetNameOption))] + public string? FleetName { get; set; } + + [InputBinding(nameof(FleetOsFamilyOption))] + public FleetCreateRequest.OsFamilyEnum? OsFamily { get; set; } + + [InputBinding(nameof(FleetRegionsOption))] + public string[]? Regions { get; set; } + + [InputBinding(nameof(FleetBuildConfigurationsOption))] + public long[]? BuildConfigurations { get; set; } + + static void ValidateRegionIds(OptionResult result) + { + var value = result.GetValueOrDefault(); + foreach (var region in value!) + { + try + { + Guid.Parse(region); + } + catch (Exception) + { + result.ErrorMessage = $"Region '{region}' not a valid UUID."; + } + } + } + + static void ValidateOsFamilyEnum(OptionResult result) + { + try + { + result.GetValueOrDefault(); + } + catch (Exception) + { + result.ErrorMessage = $"Invalid option for --os-family. Did you mean one of the following? {string.Join(", ", Enum.GetNames())}"; + } + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetIdInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetIdInput.cs new file mode 100644 index 0000000..af0ee5b --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetIdInput.cs @@ -0,0 +1,30 @@ +using System.CommandLine; +using Unity.Services.Cli.Common.Input; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +class FleetIdInput : CommonInput +{ + public const string FleetIdKey = "fleet-id"; + + public static readonly Argument FleetIdArgument = new(FleetIdKey, "The unique ID of the fleet"); + + static FleetIdInput() + { + FleetIdArgument.AddValidator(result => + { + var value = result.GetValueOrDefault(); + try + { + Guid.Parse(value); + } + catch (Exception) + { + result.ErrorMessage = $"Fleet '{value}' not a valid UUID."; + } + }); + } + + [InputBinding(nameof(FleetIdArgument))] + public string? FleetId { get; init; } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetRegionCreateInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetRegionCreateInput.cs new file mode 100644 index 0000000..d99d0ed --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetRegionCreateInput.cs @@ -0,0 +1,57 @@ +using System.CommandLine; +using Unity.Services.Cli.Common.Input; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +public class FleetRegionCreateInput : CommonInput +{ + public const string FleetIdKey = "--fleet-id"; + public const string RegionIdKey = "--region-id"; + public const string MaxServersKey = "--max-servers"; + public const string MinAvailableServersKey = "--min-available-servers"; + + public static readonly Option FleetIdOption = new( + FleetIdKey, + "The ID of the Fleet to create the Fleet Region in." + ) + { + IsRequired = true + }; + + public static readonly Option RegionIdOption = new( + RegionIdKey, + "The region ID to be added to the fleet. See the fleet region available command for a list of available regions." + ) + { + IsRequired = true + }; + + public static readonly Option MaxServersOption = new( + MaxServersKey, + "The maximum number of servers to host in the fleet region." + ) + { + IsRequired = true + }; + + public static readonly Option MinAvailableServersOption = new( + MinAvailableServersKey, + "The minimum number of servers to keep free for new game sessions." + ) + { + IsRequired = true + }; + + [InputBinding(nameof(FleetIdOption))] + public Guid? FleetId { get; set; } + + [InputBinding(nameof(RegionIdOption))] + public Guid? RegionId { get; set; } + + [InputBinding(nameof(MaxServersOption))] + public long? MaxServers { get; set; } + + [InputBinding(nameof(MinAvailableServersOption))] + public long? MinAvailableServers { get; set; } + +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetRegionUpdateInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetRegionUpdateInput.cs new file mode 100644 index 0000000..e2c74ae --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetRegionUpdateInput.cs @@ -0,0 +1,104 @@ +using System.CommandLine; +using Unity.Services.Cli.Common.Input; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +public class FleetRegionUpdateInput : CommonInput +{ + public const string FleetIdKey = "--fleet-id"; + public const string RegionIdKey = "--region-id"; + public const string DeleteTtlKey = "--delete-ttl"; + public const string DisabledDeleteTtlKey = "--disabled-delete-ttl"; + public const string MaxServersKey = "--max-servers"; + public const string MinAvailableServersKey = "--min-available-servers"; + public const string ScalingEnabledKey = "--scaling-enabled"; + public const string ShutdownTtlKey = "--shutdown-ttl"; + + public static readonly Option FleetIdOption = new( + FleetIdKey, + "The ID of the Fleet to update the Fleet Region for." + ) + { + IsRequired = true + }; + + public static readonly Option RegionIdOption = new( + RegionIdKey, + "The ID of the Region to update the Fleet Region for." + ) + { + IsRequired = true + }; + + public static readonly Option DeleteTtlOption = new( + DeleteTtlKey, + "The delete TTL set for the fleet" + ) + { + IsRequired = true + }; + + public static readonly Option DisabledDeleteTtlOption = new( + DisabledDeleteTtlKey, + "The disabled delete TTL set for the fleet." + ) + { + IsRequired = true + }; + + public static readonly Option MaxServersOption = new( + MaxServersKey, + "The maximum number of servers to host in the fleet region." + ) + { + IsRequired = true + }; + + public static readonly Option MinAvailableServersOption = new( + MinAvailableServersKey, + "The minimum number of servers to keep free for new game sessions." + ) + { + IsRequired = true + }; + + public static readonly Option ScalingEnabledOption = new( + ScalingEnabledKey, + "Whether scaling should be enabled for the fleet." + ) + { + IsRequired = true + }; + + public static readonly Option ShutdownTtlOption = new( + ShutdownTtlKey, + "The shutdown TTL set for the fleet." + ) + { + IsRequired = true + }; + + [InputBinding(nameof(FleetIdOption))] + public Guid? FleetId { get; set; } + + [InputBinding(nameof(RegionIdOption))] + public Guid? RegionId { get; set; } + + [InputBinding(nameof(DeleteTtlOption))] + public long? DeleteTtl { get; set; } + + [InputBinding(nameof(DisabledDeleteTtlOption))] + public long? DisabledDeleteTtl { get; set; } + + [InputBinding(nameof(MaxServersOption))] + public long? MaxServers { get; set; } + + [InputBinding(nameof(MinAvailableServersOption))] + public long? MinAvailableServers { get; set; } + + [InputBinding(nameof(ScalingEnabledOption))] + public bool? ScalingEnabled { get; set; } + + [InputBinding(nameof(ShutdownTtlOption))] + public long? ShutdownTtl { get; set; } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetUpdateInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetUpdateInput.cs new file mode 100644 index 0000000..efd2451 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/FleetUpdateInput.cs @@ -0,0 +1,53 @@ +using System.CommandLine; +using Unity.Services.Cli.Common.Input; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +class FleetUpdateInput : FleetIdInput +{ + public const string NameKey = "--name"; + public const string AllocTtlKey = "--allocation-ttl"; + public const string DeleteTtlKey = "--delete-ttl"; + public const string DisabledDeleteTtlKey = "--disabled-delete-ttl"; + public const string ShutdownTtlKey = "--shutdown-ttl"; + public const string BuildConfigsKey = "--build-configurations"; + + public static readonly Option FleetNameOption = new(NameKey, "The name of the fleet"); + + public static readonly Option AllocTtlOption = + new(AllocTtlKey, "The allocation TTL for the fleet"); + + public static readonly Option DeleteTtlOption = + new(DeleteTtlKey, "The delete TTL set for the fleet"); + + public static readonly Option DisabledDeleteTtlOption = + new(DisabledDeleteTtlKey, "The disabled delete TTL set for the fleet"); + + public static readonly Option ShutdownTtlOption = + new(ShutdownTtlKey, "The shutdown TTL set for the fleet"); + + public static readonly Option> BuildConfigsOption = new( + BuildConfigsKey, + "A list of build configuration IDs to associate with the fleet") + { + AllowMultipleArgumentsPerToken = true + }; + + [InputBinding(nameof(FleetNameOption))] + public string? Name { get; set; } + + [InputBinding(nameof(AllocTtlOption))] + public long? AllocTtl { get; set; } + + [InputBinding(nameof(DeleteTtlOption))] + public long? DeleteTtl { get; set; } + + [InputBinding(nameof(DisabledDeleteTtlOption))] + public long? DisabledDeleteTtl { get; set; } + + [InputBinding(nameof(ShutdownTtlOption))] + public long? ShutdownTtl { get; set; } + + [InputBinding(nameof(BuildConfigsOption))] + public List? BuildConfigs { get; set; } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/ServerIdInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/ServerIdInput.cs new file mode 100644 index 0000000..63ef6e1 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/ServerIdInput.cs @@ -0,0 +1,30 @@ +using System.CommandLine; +using Unity.Services.Cli.Common.Input; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +class ServerIdInput : CommonInput +{ + public const string ServerIdKey = "server-id"; + + public static readonly Argument ServerIdArgument = new(ServerIdKey, "The unique ID of the server"); + + static ServerIdInput() + { + ServerIdArgument.AddValidator(result => + { + var value = result.GetValueOrDefault(); + try + { + _ = long.Parse(value); + } + catch (Exception) + { + result.ErrorMessage = $"Server ID '{value}' not a valid ID."; + } + }); + } + + [InputBinding(nameof(ServerIdArgument))] + public string? ServerId { get; init; } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/ServerListInput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/ServerListInput.cs new file mode 100644 index 0000000..14427d4 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Input/ServerListInput.cs @@ -0,0 +1,95 @@ +using System.CommandLine; +using System.CommandLine.Parsing; +using Unity.Services.Cli.Common.Input; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; + +namespace Unity.Services.Cli.GameServerHosting.Input; + +public class ServerListInput : CommonInput +{ + public const string FleetIdKey = "--fleet-id"; + public const string BuildConfigurationIdKey = "--build-configuration-id"; + public const string PartialKey = "--partial"; + public const string StatusKey = "--status"; + + public static readonly Option FleetIdOption = new( + FleetIdKey, + "The fleet ID to filter the server list by." + ); + + public static readonly Option BuildConfigurationIdOption = new( + BuildConfigurationIdKey, + "The build configuration ID to filter the server list by." + ); + + public static readonly Option PartialOption = new( + PartialKey, + "The partial search to filter the server list by." + ); + + public static readonly Option StatusOption = new( + StatusKey, + "The status to filter the server list by." + ); + + static ServerListInput() + { + FleetIdOption.AddValidator(ValidateFleetId); + BuildConfigurationIdOption.AddValidator(ValidateBuildConfigurationId); + StatusOption.AddValidator(ValidateStatus); + } + + [InputBinding(nameof(FleetIdOption))] + public string? FleetId { get; set; } + + [InputBinding(nameof(BuildConfigurationIdOption))] + public string? BuildConfigurationId { get; set; } + + [InputBinding(nameof(PartialOption))] + public string? Partial { get; set; } + + [InputBinding(nameof(StatusOption))] + public string? Status { get; set; } + + static void ValidateFleetId(OptionResult result) + { + var value = result.GetValueOrDefault(); + if (value == null) return; + try + { + Guid.Parse(value); + } + catch (Exception) + { + result.ErrorMessage = $"Invalid option for --fleet-id. {value} is not a valid UUID"; + } + } + + static void ValidateBuildConfigurationId(OptionResult result) + { + var value = result.GetValueOrDefault(); + if (value == null) return; + try + { + _ = long.Parse(value); + } + catch (Exception) + { + result.ErrorMessage = $"Invalid option for --build-configuration-id. {value} is not a valid number."; + } + } + + static void ValidateStatus(OptionResult result) + { + var value = result.GetValueOrDefault(); + if (value == null) return; + try + { + Enum.Parse(value); + } + catch (Exception) + { + result.ErrorMessage = $"Invalid option for --status. Did you mean one of the following? {string.Join(", ", Enum.GetNames())}"; + } + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildConfigurationListItemOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildConfigurationListItemOutput.cs new file mode 100644 index 0000000..79f5ed7 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildConfigurationListItemOutput.cs @@ -0,0 +1,53 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class BuildConfigurationItemOutput +{ + public BuildConfigurationItemOutput(BuildConfigurationListItem buildConfiguration) + { + Name = buildConfiguration.Name; + Id = buildConfiguration.Id; + BuildName = buildConfiguration.BuildName; + BuildId = buildConfiguration.BuildID; + FleetName = buildConfiguration.FleetName; + FleetId = buildConfiguration.FleetID; + Version = buildConfiguration._Version; + CreatedAt = buildConfiguration.CreatedAt; + UpdatedAt = buildConfiguration.UpdatedAt; + } + + public string Name { get; } + + public long Id { get; } + + public string BuildName { get; } + + public long BuildId { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public string FleetName { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public Guid FleetId { get; } + + public long Version { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitNull, SerializeAs = typeof(string))] + public DateTime? CreatedAt { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitNull, SerializeAs = typeof(string))] + public DateTime? UpdatedAt { get; } + + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildConfigurationListOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildConfigurationListOutput.cs new file mode 100644 index 0000000..8d6256f --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildConfigurationListOutput.cs @@ -0,0 +1,22 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class BuildConfigurationListOutput : List +{ + public BuildConfigurationListOutput(IReadOnlyCollection? buildConfiguration) + { + if (buildConfiguration != null) AddRange(buildConfiguration.Select(bc => new BuildConfigurationItemOutput(bc))); + } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildConfigurationOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildConfigurationOutput.cs new file mode 100644 index 0000000..b149fbe --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildConfigurationOutput.cs @@ -0,0 +1,57 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class BuildConfigurationOutput +{ + public BuildConfigurationOutput(BuildConfiguration buildConfiguration) + { + Id = buildConfiguration.Id; + Name = buildConfiguration.Name; + BuildName = buildConfiguration.BuildName; + BuildId = buildConfiguration.BuildID; + FleetName = buildConfiguration.FleetName; + FleetId = buildConfiguration.FleetID; + BinaryPath = buildConfiguration.BinaryPath; + CommandLine = buildConfiguration.CommandLine; + QueryType = buildConfiguration.QueryType; + Configuration = buildConfiguration._Configuration; + Cores = buildConfiguration.Cores; + Speed = buildConfiguration.Speed; + Memory = buildConfiguration.Memory; + Version = buildConfiguration._Version; + CreatedAt = buildConfiguration.CreatedAt; + UpdatedAt = buildConfiguration.UpdatedAt; + } + + public string Name { get; } + public long Id { get; } + public string BuildName { get; } + public long BuildId { get; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public string FleetName { get; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public Guid FleetId { get; } + public string BinaryPath { get; } + public string CommandLine { get; } + public string QueryType { get; } + public List Configuration { get; } + public long Cores { get; } + public long Speed { get; } + public long Memory { get; } + public long Version { get; } + public DateTime? CreatedAt { get; } + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public DateTime? UpdatedAt { get; } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemFailuresItemOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemFailuresItemOutput.cs new file mode 100644 index 0000000..9f4eb73 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemFailuresItemOutput.cs @@ -0,0 +1,31 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class BuildInstallsItemFailuresItemOutput +{ + public BuildInstallsItemFailuresItemOutput(BuildListInner1FailuresInner failure) + { + MachineId = failure.MachineID; + Reason = failure.Reason; + Updated = failure.Updated; + } + + public long MachineId { get; } + + public string Reason { get; } + + [YamlMember(SerializeAs = typeof(string))] + public DateTime Updated { get; } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemFailuresOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemFailuresOutput.cs new file mode 100644 index 0000000..2216c2d --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemFailuresOutput.cs @@ -0,0 +1,22 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class BuildInstallsItemFailuresOutput : List +{ + public BuildInstallsItemFailuresOutput(IEnumerable failures) + { + foreach (var failure in failures) Add(new BuildInstallsItemFailuresItemOutput(failure)); + } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemOutput.cs new file mode 100644 index 0000000..4bcfd57 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemOutput.cs @@ -0,0 +1,46 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class BuildInstallsItemOutput +{ + public BuildInstallsItemOutput(BuildListInner1 install) + { + FleetName = install.FleetName; + // Ccd can be null, the codegen doesn't handle this case + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (install.Ccd != null) Ccd = new CcdOutput(install.Ccd); + Container = install.Container; + PendingMachines = install.PendingMachines; + CompletedMachines = install.CompletedMachines; + Failures = new BuildInstallsItemFailuresOutput(install.Failures); + Regions = new BuildInstallsItemRegionsOutput(install.Regions); + } + + public string FleetName { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public CcdOutput? Ccd { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public ContainerImage Container { get; } + + public long PendingMachines { get; } + + public long CompletedMachines { get; } + + public BuildInstallsItemFailuresOutput Failures { get; } + + public BuildInstallsItemRegionsOutput Regions { get; } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemRegionsItemOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemRegionsItemOutput.cs new file mode 100644 index 0000000..bfb2ac8 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemRegionsItemOutput.cs @@ -0,0 +1,30 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class BuildInstallsItemRegionsItemOutput +{ + public BuildInstallsItemRegionsItemOutput(RegionsInner region) + { + RegionName = region.RegionName; + PendingMachines = region.PendingMachines; + CompletedMachines = region.CompletedMachines; + Failures = region.Failures; + } + + public string RegionName { get; } + public long PendingMachines { get; } + public long CompletedMachines { get; } + public long Failures { get; } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemRegionsOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemRegionsOutput.cs new file mode 100644 index 0000000..f190d11 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsItemRegionsOutput.cs @@ -0,0 +1,22 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class BuildInstallsItemRegionsOutput : List +{ + public BuildInstallsItemRegionsOutput(IEnumerable regions) + { + AddRange(regions.Select(region => new BuildInstallsItemRegionsItemOutput(region))); + } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsOutput.cs new file mode 100644 index 0000000..8ab4bf5 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildInstallsOutput.cs @@ -0,0 +1,22 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class BuildInstallsOutput : List +{ + public BuildInstallsOutput(IReadOnlyCollection? installs) + { + if (installs != null) AddRange(installs.Select(b => new BuildInstallsItemOutput(b))); + } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildListOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildListOutput.cs new file mode 100644 index 0000000..0f2d1f2 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildListOutput.cs @@ -0,0 +1,22 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class BuildListOutput : List +{ + public BuildListOutput(IReadOnlyCollection? builds) + { + if (builds != null) AddRange(builds.Select(b => new BuildOutput(b))); + } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildOutput.cs new file mode 100644 index 0000000..44739be --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/BuildOutput.cs @@ -0,0 +1,70 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class BuildOutput +{ + public BuildOutput(BuildListInner build) + { + BuildName = build.BuildName; + BuildId = build.BuildID; + OsFamily = build.OsFamily; + Updated = build.Updated; + BuildConfigurations = build.BuildConfigurations; + SyncStatus = build.SyncStatus; + // Ccd can be null, the codegen doesn't handle this case + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (build.Ccd != null) Ccd = new CcdOutput(build.Ccd); + Container = build.Container; + S3 = build.S3; + } + + public BuildOutput(CreateBuild200Response build) + { + BuildName = build.BuildName; + BuildId = build.BuildID; + OsFamily = (BuildListInner.OsFamilyEnum?)build.OsFamily; + Updated = build.Updated; + SyncStatus = (BuildListInner.SyncStatusEnum)build.SyncStatus; + // Ccd can be null, the codegen doesn't handle this case + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + if (build.Ccd != null) Ccd = new CcdOutput(build.Ccd); + Container = build.Container; + S3 = build.S3; + } + + public string BuildName { get; } + + public long BuildId { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitNull)] + public BuildListInner.OsFamilyEnum? OsFamily { get; } + + [YamlMember(SerializeAs = typeof(string))] + public DateTime Updated { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitNull)] + public long? BuildConfigurations { get; } + + public BuildListInner.SyncStatusEnum SyncStatus { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public CcdOutput? Ccd { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public ContainerImage Container { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public AmazonS3Details S3 { get; } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/CcdOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/CcdOutput.cs new file mode 100644 index 0000000..4dbf0f4 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/CcdOutput.cs @@ -0,0 +1,28 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class CcdOutput +{ + public CcdOutput(CCDDetails? ccdDetails) + { + if (ccdDetails == null) return; + BucketId = ccdDetails.BucketID; + ReleaseId = ccdDetails.ReleaseID; + } + + public Guid BucketId { get; } + + public Guid ReleaseId { get; } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetGetOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetGetOutput.cs new file mode 100644 index 0000000..b7bf8ae --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetGetOutput.cs @@ -0,0 +1,62 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +public class FleetGetOutput +{ + public FleetGetOutput(Fleet fleet) + { + Name = fleet.Name; + Id = fleet.Id; + OsFamily = fleet.OsFamily; + OsName = fleet.OsName; + Status = fleet.Status; + BuildConfigurations = fleet.BuildConfigurations; + FleetRegions = fleet.FleetRegions; + Servers = fleet.Servers; + AllocationTtl = fleet.AllocationTTL; + DeleteTtl = fleet.DeleteTTL; + DisabledDeleteTtl = fleet.DisabledDeleteTTL; + ShutdownTtl = fleet.ShutdownTTL; + } + + public string Name { get; } + + public Guid Id { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitNull)] + public Fleet.OsFamilyEnum? OsFamily { get; } + + public string OsName { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitNull)] + public Fleet.StatusEnum Status { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public List BuildConfigurations { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public List FleetRegions { get; } + + [YamlMember(SerializeAs = typeof(Servers))] + public Servers Servers { get; } + + public long AllocationTtl { get; } + + public long DeleteTtl { get; } + + public long DisabledDeleteTtl { get; } + + public long ShutdownTtl { get; } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetListItemOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetListItemOutput.cs new file mode 100644 index 0000000..01fbd58 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetListItemOutput.cs @@ -0,0 +1,42 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class FleetListItemOutput +{ + public FleetListItemOutput(FleetListItem fleet) + { + Name = fleet.Name; + Id = fleet.Id; + OsName = fleet.OsName; + Status = fleet.Status; + BuildConfigurations = fleet.BuildConfigurations; + Regions = fleet.Regions; + Servers = fleet.Servers; + } + + public string Name { get; } + + public Guid Id { get; } + + public string OsName { get; } + + public FleetListItem.StatusEnum Status { get; } + + public List BuildConfigurations { get; } + + public List Regions { get; } + + public Servers Servers { get; } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetListOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetListOutput.cs new file mode 100644 index 0000000..fe5eff2 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetListOutput.cs @@ -0,0 +1,22 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class FleetListOutput : List +{ + public FleetListOutput(IReadOnlyCollection? fleets) + { + if (fleets != null) AddRange(fleets.Select(f => new FleetListItemOutput(f))); + } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetRegionCreateOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetRegionCreateOutput.cs new file mode 100644 index 0000000..a7656e1 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetRegionCreateOutput.cs @@ -0,0 +1,36 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class FleetRegionCreateOutput +{ + public FleetRegionCreateOutput(NewFleetRegion region) + { + FleetRegionId = region.Id; + MaxServers = region.MaxServers; + MinAvailableServers = region.MinAvailableServers; + RegionId = region.RegionID; + RegionName = region.RegionName; + } + + public Guid FleetRegionId { get; } + + public long MaxServers { get; } + + public long MinAvailableServers { get; } + + public Guid RegionId { get; } + + public string RegionName { get; } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetRegionUpdateOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetRegionUpdateOutput.cs new file mode 100644 index 0000000..072a4f7 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/FleetRegionUpdateOutput.cs @@ -0,0 +1,48 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class FleetRegionUpdateOutput +{ + public FleetRegionUpdateOutput(UpdatedFleetRegion region) + { + DeleteTtl = region.DeleteTTL; + DisabledDeleteTtl = region.DisabledDeleteTTL; + Id = region.Id; + MaxServers = region.MaxServers; + MinAvailableServers = region.MinAvailableServers; + RegionId = region.RegionID; + RegionName = region.RegionName; + ScalingEnabled = region.ScalingEnabled; + ShutdownTtl = region.ShutdownTTL; + } + + public long DeleteTtl { get; } + + public long DisabledDeleteTtl { get; } + + public Guid Id { get; } + + public long MaxServers { get; } + + public long MinAvailableServers { get; } + + public Guid RegionId { get; } + + public string RegionName { get; } + + public bool ScalingEnabled { get; } + + public long ShutdownTtl { get; } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/LocalFile.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/LocalFile.cs new file mode 100644 index 0000000..7b96507 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/LocalFile.cs @@ -0,0 +1,23 @@ +namespace Unity.Services.Cli.GameServerHosting.Model; + +class LocalFile +{ + readonly string m_PathInDirectory; + readonly string m_SystemPath; + + public LocalFile(string systemPath, string pathInDirectory) + { + m_SystemPath = systemPath; + m_PathInDirectory = pathInDirectory; + } + + public string GetSystemPath() + { + return m_SystemPath; + } + + public string GetPathInDirectory() + { + return m_PathInDirectory; + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/RegionTemplateListItemOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/RegionTemplateListItemOutput.cs new file mode 100644 index 0000000..14072c4 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/RegionTemplateListItemOutput.cs @@ -0,0 +1,27 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class RegionTemplateListItemOutput +{ + public RegionTemplateListItemOutput(FleetRegionsTemplateListItem region) + { + Name = region.Name; + RegionId = region.RegionID; + } + + public string Name { get; } + + public Guid RegionId { get; } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/RegionTemplateListOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/RegionTemplateListOutput.cs new file mode 100644 index 0000000..84d0ab9 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/RegionTemplateListOutput.cs @@ -0,0 +1,22 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class RegionTemplateListOutput : List +{ + public RegionTemplateListOutput(IReadOnlyCollection? regions) + { + if (regions != null) AddRange(regions.Select(f => new RegionTemplateListItemOutput(f))); + } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/ServerGetOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/ServerGetOutput.cs new file mode 100644 index 0000000..ef4a7d1 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/ServerGetOutput.cs @@ -0,0 +1,65 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +public class ServerGetOutput +{ + public ServerGetOutput(Server server) + { + BuildConfigurationId = server.BuildConfigurationID; + BuildConfigurationName = server.BuildConfigurationName; + BuildName = server.BuildName; + Deleted = server.Deleted; + FleetId = server.FleetID; + FleetName = server.FleetName; + HardwareType = server.HardwareType; + Id = server.Id; + Ip = server.Ip; + LocationId = server.LocationID; + LocationName = server.LocationName; + MachineId = server.MachineID; + Port = server.Port; + Status = server.Status; + } + + public long BuildConfigurationId { get; } + + public string BuildConfigurationName { get; } + + public string BuildName { get; } + + public bool Deleted { get; } + + public Guid FleetId { get; } + + public string FleetName { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitNull)] + public Server.HardwareTypeEnum HardwareType { get; } + + public long Id { get; } + + public string Ip { get; } + + public long LocationId { get; } + + public string LocationName { get; } + + public long MachineId { get; } + + public int Port { get; } + + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitNull)] + public Server.StatusEnum Status { get; } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/ServersItemOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/ServersItemOutput.cs new file mode 100644 index 0000000..b2cc9ea --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/ServersItemOutput.cs @@ -0,0 +1,57 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class ServersItemOutput +{ + public ServersItemOutput(Server server) + { + Id = server.Id; + Ip = server.Ip; + Port = server.Port; + MachineId = server.MachineID; + LocationId = server.LocationID; + LocationName = server.LocationName; + FleetId = server.FleetID; + FleetName = server.FleetName; + BuildConfigurationId = server.BuildConfigurationID; + BuildConfigurationName = server.BuildConfigurationName; + BuildName = server.BuildName; + Deleted = server.Deleted; + } + + public long Id { get; } + + public string Ip { get; } + + public int Port { get; } + + public long MachineId { get; } + + public string LocationName { get; } + + public long LocationId { get; } + + public string FleetName { get; } + + public Guid FleetId { get; } + + public string BuildConfigurationName { get; } + + public long BuildConfigurationId { get; } + + public string BuildName { get; } + + public bool Deleted { get; } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/ServersOutput.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/ServersOutput.cs new file mode 100644 index 0000000..8ee8003 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Model/ServersOutput.cs @@ -0,0 +1,22 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Model; + +class ServersOutput : List +{ + public ServersOutput(IReadOnlyCollection? servers) + { + if (servers != null) AddRange(servers.Select(s => new ServersItemOutput(s))); + } + + public override string ToString() + { + var serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .DisableAliases() + .Build(); + return serializer.Serialize(this); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Service/GameServerHostingService.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Service/GameServerHostingService.cs new file mode 100644 index 0000000..caedea6 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Service/GameServerHostingService.cs @@ -0,0 +1,42 @@ +using Unity.Services.Cli.ServiceAccountAuthentication; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; + +namespace Unity.Services.Cli.GameServerHosting.Service; + +public class GameServerHostingService : IGameServerHostingService +{ + readonly IServiceAccountAuthenticationService m_AuthenticationService; + + public GameServerHostingService( + IServiceAccountAuthenticationService authenticationService, + IBuildsApi buildsApi, + IBuildConfigurationsApi buildConfigurationsApi, + IFleetsApi fleetsApi, + IServersApi serversApi + ) + { + m_AuthenticationService = authenticationService; + BuildsApi = buildsApi; + BuildConfigurationsApi = buildConfigurationsApi; + FleetsApi = fleetsApi; + ServersApi = serversApi; + } + + public IBuildsApi BuildsApi { get; } + + public IBuildConfigurationsApi BuildConfigurationsApi { get; } + + public IFleetsApi FleetsApi { get; } + + public IServersApi ServersApi { get; } + + + public async Task AuthorizeGameServerHostingService(CancellationToken cancellationToken = default) + { + var token = await m_AuthenticationService.GetAccessTokenAsync(cancellationToken); + BuildsApi.Configuration.DefaultHeaders.Add("Authorization", $"Basic {token}"); + BuildConfigurationsApi.Configuration.DefaultHeaders.Add("Authorization", $"Basic {token}"); + FleetsApi.Configuration.DefaultHeaders.Add("Authorization", $"Basic {token}"); + ServersApi.Configuration.DefaultHeaders.Add("Authorization", $"Basic {token}"); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Service/IGameServerHostingService.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Service/IGameServerHostingService.cs new file mode 100644 index 0000000..26d4cc2 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Service/IGameServerHostingService.cs @@ -0,0 +1,13 @@ +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; + +namespace Unity.Services.Cli.GameServerHosting.Service; + +public interface IGameServerHostingService +{ + public IBuildsApi BuildsApi { get; } + public IBuildConfigurationsApi BuildConfigurationsApi { get; } + public IFleetsApi FleetsApi { get; } + public IServersApi ServersApi { get; } + + public Task AuthorizeGameServerHostingService(CancellationToken cancellationToken = default); +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/ApiClientFactory.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/ApiClientFactory.cs new file mode 100644 index 0000000..1388eeb --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/ApiClientFactory.cs @@ -0,0 +1,72 @@ +using System.Text; +using Unity.Services.Cli.ServiceAccountAuthentication; +using Unity.Services.Cli.ServiceAccountAuthentication.Token; +using Unity.Services.Gateway.ContentDeliveryManagementApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; +using Unity.Services.Multiplay.Authoring.Core.Builds; +using MultiplayApi = Unity.Services.Multiplay.Authoring.Core.MultiplayApi; + +namespace Unity.Services.Cli.GameServerHosting.Services; + +class ApiClientFactory : MultiplayApi.IBuildsApiFactory, MultiplayApi.IBuildConfigApiFactory, MultiplayApi.IFleetApiFactory, ICloudStorageFactory +{ + readonly IBuildsApiAsync m_BuildsApiAsync; + readonly IBuildConfigurationsApiAsync m_BuildConfigurationsApiAsync; + readonly IFleetsApiAsync m_FleetsApiAsync; + readonly IBucketsApiAsync m_BucketsApiAsync; + readonly IEntriesApiAsync m_EntriesApiAsync; + readonly IServiceAccountAuthenticationService m_AuthenticationService; + readonly GameServerHostingApiConfig m_GameServerHostingApiConfig; + + public ApiClientFactory( + IBuildsApiAsync buildsApiAsync, + IBuildConfigurationsApiAsync buildConfigurationsApiAsync, + IFleetsApiAsync fleetsApiAsync, + IBucketsApiAsync bucketsApiAsync, + IEntriesApiAsync entriesApiAsync, + IServiceAccountAuthenticationService authenticationService, + GameServerHostingApiConfig multiplayApiConfig) + { + m_BuildsApiAsync = buildsApiAsync; + m_AuthenticationService = authenticationService; + m_GameServerHostingApiConfig = multiplayApiConfig; + m_FleetsApiAsync = fleetsApiAsync; + m_BucketsApiAsync = bucketsApiAsync; + m_EntriesApiAsync = entriesApiAsync; + m_BuildConfigurationsApiAsync = buildConfigurationsApiAsync; + } + + async Task MultiplayApi.IBuildsApiFactory.Build() + { + var token = await m_AuthenticationService.GetAccessTokenAsync(); + m_BuildsApiAsync.Configuration.DefaultHeaders.SetAccessTokenHeader(token); + return new BuildClient(m_BuildsApiAsync, m_GameServerHostingApiConfig); + } + + async Task MultiplayApi.IBuildConfigApiFactory.Build() + { + var token = await m_AuthenticationService.GetAccessTokenAsync(); + m_BuildConfigurationsApiAsync.Configuration.DefaultHeaders.SetAccessTokenHeader(token); + return new BuildConfigsClient(m_BuildConfigurationsApiAsync, m_GameServerHostingApiConfig); + } + + async Task MultiplayApi.IFleetApiFactory.Build() + { + var token = await m_AuthenticationService.GetAccessTokenAsync(); + m_FleetsApiAsync.Configuration.DefaultHeaders.SetAccessTokenHeader(token); + return new FleetClient(m_FleetsApiAsync, m_GameServerHostingApiConfig); + } + + Task ICloudStorageFactory.Build() + { + var client = new HttpClient(); + SetCcdApiKey(m_BucketsApiAsync.Configuration.DefaultHeaders); + SetCcdApiKey(m_EntriesApiAsync.Configuration.DefaultHeaders); + return Task.FromResult(new CcdCloudStorageClient(m_BucketsApiAsync, m_EntriesApiAsync, client, m_GameServerHostingApiConfig)); + } + + void SetCcdApiKey(IDictionary headers) + { + headers["Authorization"] = $"Basic {Convert.ToBase64String(Encoding.UTF8.GetBytes($":{m_GameServerHostingApiConfig.CcdApiKey}"))}"; + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/BuildClient.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/BuildClient.cs new file mode 100644 index 0000000..0fbdaf8 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/BuildClient.cs @@ -0,0 +1,71 @@ +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using Unity.Services.Multiplay.Authoring.Core.Assets; +using Unity.Services.Multiplay.Authoring.Core.Builds; +using Unity.Services.Multiplay.Authoring.Core.MultiplayApi; +using IBuildsApi = Unity.Services.Multiplay.Authoring.Core.MultiplayApi.IBuildsApi; + +namespace Unity.Services.Cli.GameServerHosting.Services; + +class BuildClient : IBuildsApi +{ + readonly IBuildsApiAsync m_BuildsApiAsync; + readonly GameServerHostingApiConfig m_ApiConfig; + + public BuildClient(IBuildsApiAsync buildsApiAsync, GameServerHostingApiConfig apiConfig) + { + m_BuildsApiAsync = buildsApiAsync; + m_ApiConfig = apiConfig; + } + + public async Task<(BuildId, CloudBucketId)?> FindByName(string name, CancellationToken cancellationToken = default) + { + var res = await m_BuildsApiAsync.ListBuildsAsync( + m_ApiConfig.ProjectId, + m_ApiConfig.EnvironmentId, + partialFilter: name, + cancellationToken: cancellationToken); + switch (res.Count(b => b.BuildName == name)) + { + case 0: + return null; + case 1: + return ( + new BuildId { Id = res[0].BuildID }, + new CloudBucketId { Id = res[0].Ccd.BucketID } + ); + default: + throw new DuplicateResourceException("Build", name); + } + } + + public async Task<(BuildId, CloudBucketId)> Create(string name, MultiplayConfig.BuildDefinition definition, CancellationToken cancellationToken = default) + { + var res = await m_BuildsApiAsync.CreateBuildAsync(m_ApiConfig.ProjectId, + m_ApiConfig.EnvironmentId, + new CreateBuildRequest(name, ccd: new CCDDetails1()), cancellationToken: cancellationToken); + return ( + new BuildId { Id = res.BuildID }, + new CloudBucketId { Id = res.Ccd.BucketID } + ); + } + + public Task CreateVersion(BuildId id, CloudBucketId bucket, CancellationToken cancellationToken = default) + { + return m_BuildsApiAsync.CreateNewBuildVersionAsync(m_ApiConfig.ProjectId, + m_ApiConfig.EnvironmentId, + id.ToLong(), + new CreateNewBuildVersionRequest(ccd: new CCDDetails2(bucketID: bucket.ToGuid())), cancellationToken: cancellationToken); + } + + public async Task IsSynced(BuildId id, CancellationToken cancellationToken = default) + { + var res = await m_BuildsApiAsync.GetBuildAsync(m_ApiConfig.ProjectId, m_ApiConfig.EnvironmentId, id.ToLong(), cancellationToken: cancellationToken); + if (res.SyncStatus == CreateBuild200Response.SyncStatusEnum.FAILED) + { + throw new SyncFailedException(); + } + return res.SyncStatus == CreateBuild200Response.SyncStatusEnum.SYNCED; + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/BuildConfigsClient.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/BuildConfigsClient.cs new file mode 100644 index 0000000..ebc2ab4 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/BuildConfigsClient.cs @@ -0,0 +1,79 @@ +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using Unity.Services.Multiplay.Authoring.Core.Assets; +using Unity.Services.Multiplay.Authoring.Core.MultiplayApi; + +namespace Unity.Services.Cli.GameServerHosting.Services; + +class BuildConfigsClient : IBuildConfigApi +{ + readonly IBuildConfigurationsApiAsync m_BuildConfigurationsApiAsync; + readonly GameServerHostingApiConfig m_ApiConfig; + + public BuildConfigsClient(IBuildConfigurationsApiAsync buildConfigurationsApiAsync, GameServerHostingApiConfig apiConfig) + { + m_BuildConfigurationsApiAsync = buildConfigurationsApiAsync; + m_ApiConfig = apiConfig; + } + + public async Task FindByName(string name, CancellationToken cancellationToken = default) + { + var res = await m_BuildConfigurationsApiAsync + .ListBuildConfigurationsAsync(m_ApiConfig.ProjectId, m_ApiConfig.EnvironmentId, partialFilter: name, cancellationToken: cancellationToken); + switch (res.Count(b => b.Name == name)) + { + case 0: + return null; + case 1: + return new BuildConfigurationId { Id = res[0].Id }; + default: + throw new DuplicateResourceException("BuildConfiguration", name); + } + } + + public async Task Create(string name, BuildId buildId, MultiplayConfig.BuildConfigurationDefinition definition, CancellationToken cancellationToken = default) + { + var request = new BuildConfigurationCreateRequest( + name: name, + buildID: buildId.ToLong(), + queryType: definition.QueryType.ToString()!, + binaryPath: definition.BinaryPath, + commandLine: definition.CommandLine, + cores: definition.Cores, + speed: definition.SpeedMhz, + memory: definition.MemoryMiB, + configuration: definition.Variables.Select(kv => new ConfigurationPair(kv.Key, kv.Value)).ToList() + ); + + var res = await m_BuildConfigurationsApiAsync + .CreateBuildConfigurationAsync( + m_ApiConfig.ProjectId, + m_ApiConfig.EnvironmentId, + request, + cancellationToken: cancellationToken + ); + return new BuildConfigurationId { Id = res.Id }; + } + + public Task Update(BuildConfigurationId id, string name, BuildId buildId, MultiplayConfig.BuildConfigurationDefinition definition, CancellationToken cancellationToken = default) + { + return m_BuildConfigurationsApiAsync.UpdateBuildConfigurationAsync( + m_ApiConfig.ProjectId, + m_ApiConfig.EnvironmentId, + id.ToLong(), + new BuildConfigurationUpdateRequest( + name: name, + buildID: buildId.ToLong(), + configuration: definition.Variables.Select(kv => new ConfigurationPair1(0, kv.Key, kv.Value)).ToList(), + queryType: definition.QueryType.ToString()!, + binaryPath: definition.BinaryPath, + commandLine: definition.CommandLine, + cores: definition.Cores, + speed: definition.SpeedMhz, + memory: definition.MemoryMiB + ), + cancellationToken: cancellationToken + ); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/CcdCloudStorageClient.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/CcdCloudStorageClient.cs new file mode 100644 index 0000000..7c6423e --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/CcdCloudStorageClient.cs @@ -0,0 +1,127 @@ +using System.Net.Http.Headers; +using Unity.Services.Gateway.ContentDeliveryManagementApiV1.Generated.Api; +using Unity.Services.Gateway.ContentDeliveryManagementApiV1.Generated.Model; +using Unity.Services.Multiplay.Authoring.Core.Builds; +using Unity.Services.Multiplay.Authoring.Core.CloudContent; + +namespace Unity.Services.Cli.GameServerHosting.Services +{ + class CcdCloudStorageClient : ICloudStorage + { + const int k_BatchSize = 10; + + readonly IBucketsApiAsync m_BucketsApiClient; + readonly IEntriesApiAsync m_EntriesApiClient; + readonly HttpClient m_UploadClient; + readonly GameServerHostingApiConfig m_ApiConfig; + + public CcdCloudStorageClient( + IBucketsApiAsync bucketsApiClient, + IEntriesApiAsync entriesApiClient, + HttpClient uploadClient, + GameServerHostingApiConfig apiConfig) + { + m_BucketsApiClient = bucketsApiClient; + m_EntriesApiClient = entriesApiClient; + m_UploadClient = uploadClient; + m_ApiConfig = apiConfig; + } + + public async Task FindBucket(string name, CancellationToken cancellationToken = default) + { + var buckets = await m_BucketsApiClient.ListBucketsByProjectEnvAsync(m_ApiConfig.ProjectId.ToString(), m_ApiConfig.EnvironmentId.ToString(), name: name, cancellationToken: cancellationToken); + return buckets.Select(b => new CloudBucketId { Id = b.Id }).FirstOrDefault(); + } + + public async Task CreateBucket(string name, CancellationToken cancellationToken = default) + { + var res = await m_BucketsApiClient.CreateBucketByProjectEnvAsync(m_ApiConfig.ProjectId.ToString(), m_ApiConfig.EnvironmentId.ToString(), new CcdBucketCreate(name: name, projectguid: m_ApiConfig.ProjectId), cancellationToken: cancellationToken); + return new CloudBucketId { Id = res.Id }; + } + + public async Task UploadBuildEntries(CloudBucketId bucket, IList localEntries, CancellationToken cancellationToken = default) + { + var changes = 0; + var remoteEntries = await ListAllRemoteEntries(bucket, cancellationToken); + var orphans = remoteEntries.Keys.ToHashSet(); + + await localEntries.BatchAsync(k_BatchSize, async local => + { + var (path, content) = local; + var normalizedPath = Normalize(path); + var hash = content.CcdHash(); + if (remoteEntries.TryGetValue(normalizedPath, out var remoteEntry)) + { + orphans.Remove(normalizedPath); + if (remoteEntry.ContentHash.ToLowerInvariant() == hash) + { + return; + } + } + + var entry = await CreateOrUpdateEntry(bucket, normalizedPath, hash, (int)content.Length, cancellationToken); + changes++; + await UploadSignedContent(entry, content, cancellationToken); + }); + + await orphans.BatchAsync(k_BatchSize, async orphan => + { + var entry = remoteEntries[orphan]; + changes++; + await DeleteEntry(bucket, entry, cancellationToken); + }); + + return changes; + } + + async Task DeleteEntry(CloudBucketId bucket, CcdEntry entry, CancellationToken cancellationToken = default) + { + await m_EntriesApiClient.DeleteEntryEnvAsync(m_ApiConfig.EnvironmentId.ToString(), bucket.ToString(), entry.Entryid.ToString(), cancellationToken: cancellationToken); + } + + async Task CreateOrUpdateEntry(CloudBucketId bucket, string path, string hash, int length, CancellationToken cancellationToken = default) + { + var create = new CcdEntryCreateByPath(hash, length, signedUrl: true); + var res = await m_EntriesApiClient.CreateOrUpdateEntryByPathEnvAsync(m_ApiConfig.EnvironmentId.ToString(), bucket.ToString(), path, create, updateIfExists: true, cancellationToken: cancellationToken); + return res; + } + + async Task UploadSignedContent(CcdEntry entry, Stream content, CancellationToken cancellationToken = default) + { + // Signed uploads need to be done using HTTP Client + // Unity generated client does not support sending application/offset+octet-stream + // Timeout and retry is hard to handle here + var streamContent = new StreamContent(content); + streamContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/offset+octet-stream"); + var res = await m_UploadClient.PutAsync(entry.SignedUrl, streamContent, cancellationToken: cancellationToken); + res.EnsureSuccessStatusCode(); + } + + async Task> ListAllRemoteEntries(CloudBucketId bucket, CancellationToken cancellationToken = default) + { + const int entriesPerPage = 100; + var entries = new Dictionary(); + + List res; + var page = 1; + do + { + res = await m_EntriesApiClient.GetEntriesEnvAsync(m_ApiConfig.EnvironmentId.ToString(), bucket.ToString(), page: page, perPage: entriesPerPage, cancellationToken: cancellationToken); + + foreach (var entry in res) + { + entries.Add(entry.Path, entry); + } + page++; + } + while (res.Count == entriesPerPage); + + return entries; + } + + static string Normalize(string path) + { + return path.TrimStart('/'); + } + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/DeployFileService.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/DeployFileService.cs new file mode 100644 index 0000000..96429d6 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/DeployFileService.cs @@ -0,0 +1,50 @@ +using System.IO.Abstractions; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.GameServerHosting.Exceptions; + +namespace Unity.Services.Cli.GameServerHosting.Services; + +class DeployFileService : IDeployFileService +{ + readonly IFile m_File; + readonly IDirectory m_Directory; + + public DeployFileService(IFile file, IDirectory directory) + { + m_File = file; + m_Directory = directory; + } + + public IEnumerable ListFilesToDeploy(ICollection paths, string extension) + { + if (!paths.Any()) + { + throw new CliException("Please specify at least one path to deploy.", ExitCode.HandledError); + } + + foreach (var path in paths) + { + if (m_File.Exists(path)) + { + if (Path.GetExtension(path) != extension) + { + throw new InvalidExtensionException(path, extension); + } + yield return path; + } + else if (m_Directory.Exists(path)) + { + foreach (var configPath in m_Directory.EnumerateFiles(path, $"*{extension}", SearchOption.AllDirectories)) + { + yield return configPath; + } + } + else + { + throw new PathNotFoundException(path); + } + } + } + + public Task ReadAllTextAsync(string path, CancellationToken cancellationToken) => m_File.ReadAllTextAsync(path, cancellationToken); +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/DummyBinaryBuilder.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/DummyBinaryBuilder.cs new file mode 100644 index 0000000..892c420 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/DummyBinaryBuilder.cs @@ -0,0 +1,17 @@ +using Unity.Services.Multiplay.Authoring.Core; +using Unity.Services.Multiplay.Authoring.Core.Builds; + +namespace Unity.Services.Cli.GameServerHosting.Services; + +class DummyBinaryBuilder : IBinaryBuilder +{ + public ServerBuild BuildLinuxServer(string outDir, string executable) + { + return new ServerBuild(Path.Combine(outDir, executable)); + } + + public void RevertToOriginalBuildTarget() + { + + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/FileReaderAdapter.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/FileReaderAdapter.cs new file mode 100644 index 0000000..3bd9118 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/FileReaderAdapter.cs @@ -0,0 +1,21 @@ +using System.IO.Abstractions; +using Unity.Services.Multiplay.Authoring.Core.Builds; + +namespace Unity.Services.Cli.GameServerHosting.Services; + +class FileReaderAdapter : IFileReader +{ + readonly IFile m_File; + readonly IDirectory m_Directory; + + public FileReaderAdapter(IFile file, IDirectory directory) + { + m_File = file; + m_Directory = directory; + } + + public IEnumerable EnumerateDirectories(string path) => m_Directory.EnumerateDirectories(path); + public IEnumerable EnumerateFiles(string path) => m_Directory.EnumerateFiles(path); + public Stream OpenReadFile(string path) => m_File.OpenRead(path); + public bool DirectoryExists(string path) => m_Directory.Exists(path); +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/FleetClient.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/FleetClient.cs new file mode 100644 index 0000000..5c1a4fe --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/FleetClient.cs @@ -0,0 +1,102 @@ +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Api; +using Unity.Services.Gateway.GameServerHostingApiV1.Generated.Model; +using Unity.Services.Multiplay.Authoring.Core.Assets; +using Unity.Services.Multiplay.Authoring.Core.MultiplayApi; + +namespace Unity.Services.Cli.GameServerHosting.Services; + +class FleetClient : IFleetApi +{ + readonly IFleetsApiAsync m_FleetsApiAsync; + readonly GameServerHostingApiConfig m_ApiConfig; + + public FleetClient(IFleetsApiAsync fleetsApiAsync, GameServerHostingApiConfig apiConfig) + { + m_FleetsApiAsync = fleetsApiAsync; + m_ApiConfig = apiConfig; + } + + public async Task FindByName(string name, CancellationToken cancellationToken = default) + { + var res = await m_FleetsApiAsync.ListFleetsAsync(m_ApiConfig.ProjectId, m_ApiConfig.EnvironmentId, cancellationToken: cancellationToken); + var filtered = res.Where(f => f.Name == name).ToList(); + switch (filtered.Count(b => b.Name == name)) + { + case 0: + return null; + case 1: + return new FleetId { Id = filtered[0].Id }; + default: + throw new DuplicateResourceException("BuildConfiguration", name); + } + } + + public async Task Create(string name, IList buildConfigurations, MultiplayConfig.FleetDefinition definition, CancellationToken cancellationToken = default) + { + var regions = await GetRegions(cancellationToken); + + var fleet = new FleetCreateRequest( + name: name, + buildConfigurations: buildConfigurations.Select(b => b.ToLong()).ToList(), + regions: definition.Regions.Select(r => new Region( + regionID: regions[r.Key], + minAvailableServers: r.Value.MinAvailable, + maxServers: r.Value.MaxServers)).ToList(), + osID: Guid.Empty, // Must be set in order to avoid breaking the API + osFamily: FleetCreateRequest.OsFamilyEnum.LINUX); + var res = await m_FleetsApiAsync.CreateFleetAsync(m_ApiConfig.ProjectId, m_ApiConfig.EnvironmentId, fleet, cancellationToken: cancellationToken); + return new FleetId { Id = res.Id }; + } + + public async Task Update(FleetId id, string name, IList buildConfigurations, MultiplayConfig.FleetDefinition definition, CancellationToken cancellationToken = default) + { + var fleet = new FleetUpdateRequest( + name: name, + osID: Guid.Empty, // Must be set in order to avoid breaking the API + buildConfigurations: buildConfigurations.Select(b => b.ToLong()).ToList() + ); + var res = await m_FleetsApiAsync.UpdateFleetAsync(m_ApiConfig.ProjectId, m_ApiConfig.EnvironmentId, id.ToGuid(), fleet, cancellationToken: cancellationToken); + + await UpdateRegions(id, res, definition, cancellationToken); + } + + async Task UpdateRegions(FleetId id, Fleet fleet, MultiplayConfig.FleetDefinition definition, CancellationToken cancellationToken = default) + { + var regions = await GetRegions(cancellationToken); + + var existingRegions = fleet.FleetRegions.ToDictionary(k => k.RegionName); + foreach (var (regionName, region) in definition.Regions) + { + if (!existingRegions.ContainsKey(regionName)) + { + var regionDefinition = new AddRegionRequest( + regionID: regions[regionName], + minAvailableServers: region.MinAvailable, + maxServers: region.MaxServers); + await m_FleetsApiAsync.AddFleetRegionAsync(m_ApiConfig.ProjectId, m_ApiConfig.EnvironmentId, id.ToGuid(), regionDefinition, cancellationToken: cancellationToken); + } + else + { + var regionId = existingRegions[regionName].RegionID; + var regionDefinition = new UpdateRegionRequest( + scalingEnabled: true, + minAvailableServers: region.MinAvailable, + maxServers: region.MaxServers); + await m_FleetsApiAsync + .UpdateFleetRegionAsync(m_ApiConfig.ProjectId, m_ApiConfig.EnvironmentId, id.ToGuid(), regionId, regionDefinition, cancellationToken: cancellationToken); + } + } + + foreach (var toRemove in fleet.FleetRegions.Where(r => !definition.Regions.ContainsKey(r.RegionName))) + { + await m_FleetsApiAsync.UpdateFleetRegionAsync(m_ApiConfig.ProjectId, m_ApiConfig.EnvironmentId, id.ToGuid(), toRemove.RegionID, cancellationToken: cancellationToken); + } + } + + async Task> GetRegions(CancellationToken cancellationToken = default) + { + var res = await m_FleetsApiAsync.ListTemplateFleetRegionsAsync(m_ApiConfig.ProjectId, m_ApiConfig.EnvironmentId, cancellationToken: cancellationToken); + return res.ToDictionary(r => r.Name, r => r.RegionID); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/GameServerHostingApiConfig.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/GameServerHostingApiConfig.cs new file mode 100644 index 0000000..a7c4f6e --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/GameServerHostingApiConfig.cs @@ -0,0 +1,8 @@ +namespace Unity.Services.Cli.GameServerHosting.Services; + +public class GameServerHostingApiConfig +{ + public Guid ProjectId { get; set; } + public Guid EnvironmentId { get; set; } + public string? CcdApiKey { get; set; } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/GameServerHostingConfigLoader.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/GameServerHostingConfigLoader.cs new file mode 100644 index 0000000..e81be57 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/GameServerHostingConfigLoader.cs @@ -0,0 +1,87 @@ +using Microsoft.Extensions.Logging; +using Unity.Services.Cli.GameServerHosting.Exceptions; +using Unity.Services.Multiplay.Authoring.Core.Assets; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Unity.Services.Cli.GameServerHosting.Services; + +class GameServerHostingConfigLoader : IGameServerHostingConfigLoader +{ + const string k_Extension = ".gsh"; + + readonly IDeployFileService m_DeployFileService; + readonly IMultiplayConfigValidator m_ConfigValidator; + readonly ILogger m_Logger; + + public GameServerHostingConfigLoader(IDeployFileService deployFileService, IMultiplayConfigValidator configValidator, ILogger logger) + { + m_DeployFileService = deployFileService; + m_ConfigValidator = configValidator; + m_Logger = logger; + } + + public async Task LoadAndValidateAsync(ICollection paths, CancellationToken cancellationToken) + { + var configLoadTasks = m_DeployFileService + .ListFilesToDeploy(paths, k_Extension) + .Select(async path => (path, config: await LoadConfig(m_DeployFileService, path, cancellationToken))) + .ToList(); + var configs = await Task.WhenAll(configLoadTasks); + + ValidateConfigs(m_Logger, m_ConfigValidator, configs); + var merged = MergeConfigs(configs.Select(c => c.config)); + + return merged; + } + + static void ValidateConfigs(ILogger logger, IMultiplayConfigValidator configValidator, IEnumerable<(string path, MultiplayConfig config)> configs) + { + var errors = new List<(string path, IMultiplayConfigValidator.Error error)>(); + foreach (var (path, config) in configs) + { + errors.AddRange(configValidator.Validate(config).Select(error => (path, error))); + } + + if (errors.Any()) + { + foreach (var (path, error) in errors) + { + logger.LogError("{}: {}", path, error.Message); + } + + throw new InvalidConfigException(errors.First().path); + } + } + + static async Task LoadConfig(IDeployFileService fileService, string path, CancellationToken cancellationToken) + { + var deserializer = new DeserializerBuilder() + .WithTypeConverter(new ResourceNameTypeConverter()) + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .Build(); + + return deserializer.Deserialize(await fileService.ReadAllTextAsync(path, cancellationToken)); + } + + static MultiplayConfig MergeConfigs(IEnumerable configs) + { + var merged = new MultiplayConfig + { + Version = "1.0" + }; + foreach (var config in configs) + { + foreach (var build in config.Builds ?? new Dictionary()) + merged.Builds.Add(build); + + foreach (var buildConfig in config.BuildConfigurations ?? new Dictionary()) + merged.BuildConfigurations.Add(buildConfig); + + foreach (var fleet in config.Fleets ?? new Dictionary()) + merged.Fleets.Add(fleet); + } + return merged; + } + +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/IDeployFileService.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/IDeployFileService.cs new file mode 100644 index 0000000..524be07 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/IDeployFileService.cs @@ -0,0 +1,7 @@ +namespace Unity.Services.Cli.GameServerHosting.Services; + +interface IDeployFileService +{ + public IEnumerable ListFilesToDeploy(ICollection paths, string extension); + public Task ReadAllTextAsync(string path, CancellationToken cancellationToken); +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/IGameServerHostingConfigLoader.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/IGameServerHostingConfigLoader.cs new file mode 100644 index 0000000..3a8eeee --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/IGameServerHostingConfigLoader.cs @@ -0,0 +1,8 @@ +using Unity.Services.Multiplay.Authoring.Core.Assets; + +namespace Unity.Services.Cli.GameServerHosting.Services; + +interface IGameServerHostingConfigLoader +{ + Task LoadAndValidateAsync(ICollection paths, CancellationToken cancellationToken); +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/ResourceNameTypeConverter.cs b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/ResourceNameTypeConverter.cs new file mode 100644 index 0000000..b65bd0c --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Services/ResourceNameTypeConverter.cs @@ -0,0 +1,36 @@ +using Unity.Services.Multiplay.Authoring.Core.Assets; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; + +namespace Unity.Services.Cli.GameServerHosting.Services; + +sealed class ResourceNameTypeConverter : IYamlTypeConverter +{ + public bool Accepts(Type type) + { + return typeof(IResourceName).IsAssignableFrom(type); + } + + public object? ReadYaml(IParser parser, Type type) + { + var scalar = parser.Current as Scalar; + parser.MoveNext(); + + if (scalar == null) + { + throw new InvalidDataException("Failed to retrieve scalar value."); + } + + var inst = Activator.CreateInstance(type); + type.GetProperty(nameof(IResourceName.Name))?.SetValue(inst, scalar.Value); + + return inst; + } + + public void WriteYaml(IEmitter emitter, object? value, Type type) + { + var name = (IResourceName)value!; + emitter.Emit(new Scalar(null, name.Name)); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Unity.Services.Cli.GameServerHosting.csproj b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Unity.Services.Cli.GameServerHosting.csproj new file mode 100644 index 0000000..6c24145 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.GameServerHosting/Unity.Services.Cli.GameServerHosting.csproj @@ -0,0 +1,34 @@ + + + net6.0 + 10 + enable + enable + true + + + + + + + <_Parameter1>$(AssemblyName).UnitTest + + + <_Parameter1>DynamicProxyGenAssembly2 + + + + + + + + + + + + + + + $(DefineConstants);$(ExtraDefineConstants) + + \ No newline at end of file diff --git a/Unity.Services.Cli/Unity.Services.Cli.Integration.MockServer/ServiceMocks/GameServerHosting/GameServerHostingApiMock.cs b/Unity.Services.Cli/Unity.Services.Cli.Integration.MockServer/ServiceMocks/GameServerHosting/GameServerHostingApiMock.cs index 885f49b..248141e 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Integration.MockServer/ServiceMocks/GameServerHosting/GameServerHostingApiMock.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Integration.MockServer/ServiceMocks/GameServerHosting/GameServerHostingApiMock.cs @@ -36,6 +36,12 @@ public void CustomMock(WireMockServer mockServer) MockBuild(mockServer); MockBuildList(mockServer); MockBuildCreateResponse(mockServer); + MockFleetRegionCreateResponse(mockServer); + MockFleetAvailableRegionsResponse(mockServer); + MockBuildConfigurationCreate(mockServer); + MockBuildConfigurationGet(mockServer); + MockBuildConfigurationUpdate(mockServer); + MockBuildConfigurationList(mockServer); } static void MockFleetGet(WireMockServer mockServer) @@ -80,11 +86,13 @@ static void MockServerGet(WireMockServer mockServer) fleetID: Guid.Parse(Keys.ValidFleetId), fleetName: "Test Fleet", hardwareType: Server.HardwareTypeEnum.CLOUD, - id: Int64.Parse(Keys.ValidServerId), + id: long.Parse(Keys.ValidServerId), ip: "1.1.1.1", locationID: 0, locationName: "Test Location", machineID: 123, + machineName: "test machine", + machineSpec: new MachineSpec("test-cpu"), port: 0, status: Server.StatusEnum.ONLINE ); @@ -95,7 +103,6 @@ static void MockServerGet(WireMockServer mockServer) var response = Response.Create() .WithBodyAsJson(server); - mockServer.Given(request).RespondWith(response); } @@ -111,11 +118,13 @@ static void MockServerList(WireMockServer mockServer) fleetID: Guid.Parse(Keys.ValidFleetId), fleetName: "Test Fleet", hardwareType: Server.HardwareTypeEnum.CLOUD, - id: Int64.Parse(Keys.ValidServerId), + id: long.Parse(Keys.ValidServerId), ip: "1.1.1.1", locationID: 0, locationName: "Test Location", machineID: 123, + machineName: "test machine", + machineSpec: new MachineSpec("test-cpu"), port: 0, status: Server.StatusEnum.ONLINE ) @@ -168,7 +177,6 @@ static void MockBuildList(WireMockServer mockServer) static void MockBuild(WireMockServer mockServer) { - Console.WriteLine(Keys.ValidBuildPath); var build = new CreateBuild200Response( 1, "name1", @@ -184,6 +192,97 @@ static void MockBuild(WireMockServer mockServer) .WithStatusCode(HttpStatusCode.OK); mockServer.Given(request).RespondWith(response); + + MockBuildBucket(mockServer); + MockBuildContainer(mockServer); + MockBuildFileUpload(mockServer); + } + + static void MockBuildBucket(WireMockServer mockServer) + { + var build = new CreateBuild200Response( + Keys.ValidBuildIdBucket, + "Bucket Build", + CreateBuild200Response.BuildTypeEnum.S3, + s3: new AmazonS3Details("bucket-url") + ); + + var request = Request.Create() + .WithPath(Keys.ValidBuildPathBucket) + .UsingGet(); + + var response = Response.Create() + .WithBodyAsJson(build) + .WithStatusCode(HttpStatusCode.OK); + + mockServer.Given(request).RespondWith(response); + } + + static void MockBuildContainer(WireMockServer mockServer) + { + var build = new CreateBuild200Response( + Keys.ValidBuildIdContainer, + "Container Build", + CreateBuild200Response.BuildTypeEnum.CONTAINER, + container: new ContainerImage("v1") + ); + + var request = Request.Create() + .WithPath(Keys.ValidBuildPathContainer) + .UsingGet(); + + var response = Response.Create() + .WithBodyAsJson(build) + .WithStatusCode(HttpStatusCode.OK); + + mockServer.Given(request).RespondWith(response); + } + + static void MockBuildFileUpload(WireMockServer mockServer) + { + var build = new CreateBuild200Response( + Keys.ValidBuildIdFileUpload, + "File Upload Build", + CreateBuild200Response.BuildTypeEnum.FILEUPLOAD, + ccd: new CCDDetails() + ); + + var request = Request.Create() + .WithPath(Keys.ValidBuildPathFileUpload) + .UsingGet(); + + var response = Response.Create() + .WithBodyAsJson(build) + .WithStatusCode(HttpStatusCode.OK); + + mockServer.Given(request).RespondWith(response); + + const string signedUrlPath = $"/signedUrl/{TempFileName}"; + var file = new BuildFilesListResultsInner( + "fake-hash", + TempFileName, + $"{mockServer.Url}{signedUrlPath}" + ); + + + var fileRequest = Request.Create() + .WithPath(Keys.ValidBuildPathFileUploadFiles) + .UsingPut(); + + var fileResponse = Response.Create() + .WithBodyAsJson(file) + .WithStatusCode(HttpStatusCode.OK); + + mockServer.Given(fileRequest).RespondWith(fileResponse); + + var signedUrlRequest = Request.Create() + .WithPath(signedUrlPath) + .UsingPut(); + + var signedUrlResponse = Response.Create() + .WithStatusCode(HttpStatusCode.OK); + + mockServer.Given(signedUrlRequest).RespondWith(signedUrlResponse); } static void MockBuildInstalls(WireMockServer mockServer) @@ -252,4 +351,186 @@ static void MockBuildCreateResponse(WireMockServer mockServer) .WithStatusCode(HttpStatusCode.OK); mockServer.Given(request).RespondWith(response); } + + static void MockBuildConfigurationCreate(WireMockServer mockServer) + { + var buildConfig = new BuildConfiguration + ( + binaryPath: "simple-game-server-go", + buildID: Keys.ValidBuildConfigurationId, + buildName: "Build 1", + commandLine: "--init game.init", + configuration: new List(), + cores: 1, + createdAt: new DateTime(2022, 10, 11), + fleetID: new Guid(Keys.ValidFleetId), + fleetName: "Fleet 1", + id: 1120778, + memory: 100, + name: "Testing BC", + queryType: "sqp", + speed: 100, + updatedAt: new DateTime(2022, 10, 11), + version: 5 + ); + + var request = Request.Create() + .WithPath(Keys.BuildConfigurationsPath) + .UsingPost(); + + var response = Response.Create() + .WithBodyAsJson(buildConfig) + .WithStatusCode(HttpStatusCode.OK); + + mockServer.Given(request).RespondWith(response); + } + + static void MockBuildConfigurationGet(WireMockServer mockServer) + { + var buildConfig = new BuildConfiguration + ( + binaryPath: "simple-game-server-go", + buildID: Keys.ValidBuildConfigurationId, + buildName: "Build 1", + commandLine: "--init game.init", + configuration: new List(), + cores: 1, + createdAt: new DateTime(2022, 10, 11), + fleetID: new Guid(Keys.ValidFleetId), + fleetName: "Fleet 1", + id: 1120778, + memory: 100, + name: "Testing BC", + queryType: "sqp", + speed: 100, + updatedAt: new DateTime(2022, 10, 11), + version: 5 + ); + + var request = Request.Create() + .WithPath(Keys.ValidBuildConfigurationPath) + .UsingGet(); + + var response = Response.Create() + .WithBodyAsJson(buildConfig) + .WithStatusCode(HttpStatusCode.OK); + + mockServer.Given(request).RespondWith(response); + } + + static void MockBuildConfigurationUpdate(WireMockServer mockServer) + { + var buildConfig = new BuildConfiguration + ( + binaryPath: "simple-game-server-go", + buildID: Keys.ValidBuildConfigurationId, + buildName: "Build 1", + commandLine: "--init game.init", + configuration: new List(), + cores: 1, + createdAt: new DateTime(2022, 10, 11), + fleetID: new Guid(Keys.ValidFleetId), + fleetName: "Fleet 1", + id: 1120778, + memory: 100, + name: "Testing BC", + queryType: "sqp", + speed: 100, + updatedAt: new DateTime(2022, 10, 11), + version: 5 + ); + + var request = Request.Create() + .WithPath(Keys.ValidBuildConfigurationPath) + .UsingPost(); + + var response = Response.Create() + .WithBodyAsJson(buildConfig) + .WithStatusCode(HttpStatusCode.OK); + + mockServer.Given(request).RespondWith(response); + } + + static void MockBuildConfigurationList(WireMockServer mockServer) + { + var buildConfigs = new List + { + new( + 1, + "build config 1", + new DateTime(2022, 10, 11), + new Guid(Keys.ValidFleetId), + "fleet name", + 11, + "config name", + new DateTime(2022, 10, 11), + 1 + ), + new( + 2, + "build config 2", + new DateTime(2022, 10, 11), + new Guid(Keys.ValidFleetId), + "fleet name", + 11, + "config name", + new DateTime(2022, 10, 11), + 1 + ), + }; + + var request = Request.Create() + .WithPath(Keys.BuildConfigurationsPath) + .UsingGet(); + + var response = Response.Create() + .WithBodyAsJson(buildConfigs) + .WithStatusCode(HttpStatusCode.OK); + + mockServer.Given(request).RespondWith(response); + } + + static void MockFleetRegionCreateResponse(WireMockServer mockServer) + { + var build = new NewFleetRegion( + new Guid(Keys.ValidFleetRegionId), + 2, + 1, + regionID: new Guid(Keys.ValidRegionId), + regionName: "region name" + ); + var request = Request.Create() + .WithPath(Keys.FleetRegionsPath) + .UsingPost(); + + var response = Response.Create() + .WithBodyAsJson(build) + .WithStatusCode(HttpStatusCode.OK); + mockServer.Given(request).RespondWith(response); + } + + static void MockFleetAvailableRegionsResponse(WireMockServer mockServer) + { + var resp = new List + { + new( + "US East", + new Guid(Keys.ValidRegionId) + ), + new( + "US West", + new Guid(Keys.ValidRegionIdAlt) + ) + }; + var request = Request.Create() + .WithPath(Keys.AvailableRegionsPath) + .UsingGet(); + + var response = Response.Create() + .WithBodyAsJson(resp) + .WithStatusCode(HttpStatusCode.OK); + mockServer.Given(request).RespondWith(response); + } + + public const string TempFileName = "temp-file.txt"; } diff --git a/Unity.Services.Cli/Unity.Services.Cli.Integration.MockServer/ServiceMocks/GameServerHosting/Keys.cs b/Unity.Services.Cli/Unity.Services.Cli.Integration.MockServer/ServiceMocks/GameServerHosting/Keys.cs index a5f5622..9863497 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Integration.MockServer/ServiceMocks/GameServerHosting/Keys.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Integration.MockServer/ServiceMocks/GameServerHosting/Keys.cs @@ -9,10 +9,15 @@ public static class Keys public const long ValidBuildConfigurationId = 1; public const string ValidFleetId = "00000000-0000-0000-1000-000000000000"; public const string ValidRegionId = "00000000-0000-0000-0000-100000000000"; + public const string ValidRegionIdAlt = "00000000-0000-0000-0000-100000000001"; + public const string ValidFleetRegionId = "00000000-0000-0000-0000-200000000000"; public const string ValidTemplateRegionId = "00000000-0000-0000-0000-1a0000000000"; public const long ValidBuildId = 1; public const string ValidBucketId = "00000000-0000-0000-0000-000000000000"; public const string ValidReleaseId = "00000000-0000-0000-0000-000000000000"; + public const long ValidBuildIdBucket = 101; + public const long ValidBuildIdContainer = 102; + public const long ValidBuildIdFileUpload = 103; public const string ValidServerId = "123"; @@ -24,8 +29,17 @@ public static class Keys public const string ValidFleetPath = $"{FleetsPath}/{ValidFleetId}"; public const string ValidServersPath = $"{ServersPath}/{ValidServerId}"; + public const string AvailableRegionsPath = $"{ValidFleetPath}/available-regions"; + public const string FleetRegionsPath = $"{ValidFleetPath}/regions"; public const string BuildsPath = $"/multiplay/builds/v1/{ProjectPathPart}/builds"; + public const string BuildConfigurationsPath = $"/multiplay/build-configurations/v1/{ProjectPathPart}/build-configurations"; + public static readonly string ValidBuildConfigurationPath = $"{BuildConfigurationsPath}/{ValidBuildConfigurationId}"; public static readonly string ValidBuildPath = $"{BuildsPath}/{ValidBuildId}"; public static readonly string ValidBuildInstallsPath = $"{ValidBuildPath}/installs"; + + public static readonly string ValidBuildPathBucket = $"{BuildsPath}/{ValidBuildIdBucket}"; + public static readonly string ValidBuildPathContainer = $"{BuildsPath}/{ValidBuildIdContainer}"; + public static readonly string ValidBuildPathFileUpload = $"{BuildsPath}/{ValidBuildIdFileUpload}"; + public static readonly string ValidBuildPathFileUploadFiles = $"{BuildsPath}/{ValidBuildIdFileUpload}/files"; } diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationCreateTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationCreateTests.cs new file mode 100644 index 0000000..8a5c0c0 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationCreateTests.cs @@ -0,0 +1,182 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + static readonly string k_BuildConfigurationCreatePrefix = "gsh bc create "; + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc create")] + public async Task BuildConfigurationCreate_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(k_BuildConfigurationCreatePrefix + k_BuildConfigurationCreateOrUpdateCommandComplete) + .AssertStandardOutput( + v => + { + StringAssert.Contains("Creating build config...", v); + StringAssert.Contains("binaryPath: ", v); + }) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc create")] + public async Task BuildConfigurationCreate_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_BuildConfigurationCreatePrefix + k_BuildConfigurationCreateOrUpdateCommandComplete) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc create")] + public async Task BuildConfigurationCreate_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await GetLoggedInCli() + .Command(k_BuildConfigurationCreatePrefix + k_BuildConfigurationCreateOrUpdateCommandComplete) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc create")] + public async Task BuildConfigurationCreate_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + + await GetLoggedInCli() + .Command(k_BuildConfigurationCreatePrefix + k_BuildConfigurationCreateOrUpdateCommandComplete) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc create")] + public async Task BuildConfigurationCreate_FailsMissingBinaryPathException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationCreatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingBinaryPath) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--binary-path' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc create")] + public async Task BuildConfigurationCreate_FailsMissingBuildException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationCreatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingBuild) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--build' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc create")] + public async Task BuildConfigurationCreate_FailsMissingCommandLineException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationCreatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingCommandLine) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--command-line' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc create")] + public async Task BuildConfigurationCreate_FailsMissingCoresException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationCreatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingCores) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--cores' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc create")] + public async Task BuildConfigurationCreate_FailsMissingMemoryException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationCreatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingMemory) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--memory' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc create")] + public async Task BuildConfigurationCreate_FailsMissingNameException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationCreatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingName) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--name' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc create")] + public async Task BuildConfigurationCreate_FailsMissingQueryTypeException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationCreatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingQueryType) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--query-type' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc create")] + public async Task BuildConfigurationCreate_FailsMissingSpeedException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationCreatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingSpeed) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--speed' is required.") + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationDeleteTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationDeleteTests.cs new file mode 100644 index 0000000..514cae0 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationDeleteTests.cs @@ -0,0 +1,86 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + static readonly string k_BuildConfigurationDeleteCommand = "gsh bc delete 1"; + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc delete")] + public async Task BuildConfigurationDelete_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(k_BuildConfigurationDeleteCommand) + .AssertStandardOutputContains("Deleting build configuration...") + .AssertStandardErrorContains("Build configuration deleted successfully") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc delete")] + public async Task BuildConfigurationDelete_ThrowsMissingBuildConfigurationIdException() + { + await GetFullySetCli() + .Command("gsh bc delete") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Required argument missing for command: 'delete'.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc delete")] + public async Task BuildConfigurationDelete_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_BuildConfigurationDeleteCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc delete")] + public async Task BuildConfigurationDelete_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await GetLoggedInCli() + .Command(k_BuildConfigurationDeleteCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc delete")] + public async Task BuildConfigurationDelete_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + + await GetLoggedInCli() + .Command(k_BuildConfigurationDeleteCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationGetTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationGetTests.cs new file mode 100644 index 0000000..589999e --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationGetTests.cs @@ -0,0 +1,104 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + static readonly string k_BuildConfigurationGetCommand = "gsh bc get 1"; + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc get")] + public async Task BuildConfigurationGet_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(k_BuildConfigurationGetCommand) + .AssertStandardOutput( + v => + { + StringAssert.Contains("Fetching build configuration...", v); + StringAssert.Contains("binaryPath: ", v); + }) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc get")] + public async Task BuildConfigurationGet_ThrowsMissingBuildConfigurationIdException() + { + await GetFullySetCli() + .Command("gsh bc get") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Required argument missing for command: 'get'.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc get")] + public async Task BuildConfigurationGet_ThrowsInvalidBuildConfigurationIdException() + { + await GetFullySetCli() + .Command("gsh bc get invalid-id") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Build Configuration ID 'invalid-id' not a valid ID.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc get")] + public async Task BuildConfigurationGet_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_BuildConfigurationGetCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc get")] + public async Task BuildConfigurationGet_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await GetLoggedInCli() + .Command(k_BuildConfigurationGetCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc get")] + public async Task BuildConfigurationGet_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + + await GetLoggedInCli() + .Command(k_BuildConfigurationGetCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationListTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationListTests.cs new file mode 100644 index 0000000..9aa143f --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationListTests.cs @@ -0,0 +1,78 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + static readonly string k_BuildConfigurationListCommand = "gsh bc list"; + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc list")] + public async Task BuildConfigurationList_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(k_BuildConfigurationListCommand) + .AssertStandardOutput( + v => + { + StringAssert.Contains("Fetching build configuration list...", v); + StringAssert.Contains("name: ", v); + }) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc list")] + public async Task BuildConfigurationList_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_BuildConfigurationListCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc list")] + public async Task BuildConfigurationList_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await GetLoggedInCli() + .Command(k_BuildConfigurationListCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc get")] + public async Task BuildConfigurationList_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + + await GetLoggedInCli() + .Command(k_BuildConfigurationListCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationUpdateTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationUpdateTests.cs new file mode 100644 index 0000000..6d03225 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildConfigurationUpdateTests.cs @@ -0,0 +1,182 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + static readonly string k_BuildConfigurationUpdatePrefix = "gsh bc update 1 "; + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc update")] + public async Task BuildConfigurationUpdate_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(k_BuildConfigurationUpdatePrefix + k_BuildConfigurationCreateOrUpdateCommandComplete) + .AssertStandardOutput( + v => + { + StringAssert.Contains("Updating build config...", v); + StringAssert.Contains("binaryPath: ", v); + }) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc update")] + public async Task BuildConfigurationUpdate_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_BuildConfigurationUpdatePrefix + k_BuildConfigurationCreateOrUpdateCommandComplete) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc update")] + public async Task BuildConfigurationUpdate_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await GetLoggedInCli() + .Command(k_BuildConfigurationUpdatePrefix + k_BuildConfigurationCreateOrUpdateCommandComplete) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc update")] + public async Task BuildConfigurationUpdate_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + + await GetLoggedInCli() + .Command(k_BuildConfigurationUpdatePrefix + k_BuildConfigurationCreateOrUpdateCommandComplete) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc update")] + public async Task BuildConfigurationUpdate_FailsMissingBinaryPathException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationUpdatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingBinaryPath) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--binary-path' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc update")] + public async Task BuildConfigurationUpdate_FailsMissingBuildException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationUpdatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingBuild) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--build' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc update")] + public async Task BuildConfigurationUpdate_FailsMissingCommandLineException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationUpdatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingCommandLine) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--command-line' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc update")] + public async Task BuildConfigurationUpdate_FailsMissingCoresException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationUpdatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingCores) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--cores' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc update")] + public async Task BuildConfigurationUpdate_FailsMissingMemoryException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationUpdatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingMemory) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--memory' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc update")] + public async Task BuildConfigurationUpdate_FailsMissingNameException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationUpdatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingName) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--name' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc update")] + public async Task BuildConfigurationUpdate_FailsMissingQueryTypeException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationUpdatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingQueryType) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--query-type' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh bc")] + [Category("gsh bc update")] + public async Task BuildConfigurationUpdate_FailsMissingSpeedException() + { + await GetFullySetCli() + .Command(k_BuildConfigurationUpdatePrefix + k_BuildConfigurationCreateOrUpdateCommandMissingSpeed) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--speed' is required.") + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildCreateTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildCreateTests.cs new file mode 100644 index 0000000..98a4170 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildCreateTests.cs @@ -0,0 +1,142 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + const string k_BuildCreateCommand = "gsh build create --name test-build --os-family linux --type CONTAINER"; + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create")] + public async Task BuildCreate_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(k_BuildCreateCommand) + .AssertStandardOutput( + v => + { + StringAssert.Contains("Creating build...", v); + StringAssert.Contains("buildId: ", v); + }) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create")] + public async Task BuildCreate_FailsMissingNameException() + { + await GetFullySetCli() + .Command("gsh build create --os-family linux --type CONTAINER") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--name' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create")] + public async Task BuildCreate_ThrowsMissingOsFamilyException() + { + await GetFullySetCli() + .Command("gsh build create --name test-build --type CONTAINER") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--os-family' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create")] + public async Task BuildCreate_ThrowsInvalidTypeException() + { + await GetFullySetCli() + .Command("gsh build create --name test-build --os-family linux --type VM") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains( + "Invalid option for --type. Did you mean one of the following? FILEUPLOAD, CONTAINER, S3") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create")] + public async Task BuildCreate_ThrowsMissingTypeException() + { + await GetFullySetCli() + .Command("gsh build create --name test-build --os-family linux") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--type' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create")] + public async Task BuildCreate_ThrowsInvalidOsFamilyException() + { + await GetFullySetCli() + .Command("gsh build create --name test-build --os-family solaris --type CONTAINER") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Invalid option for --os-family. Did you mean one of the following? LINUX") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create")] + public async Task BuildCreate_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_BuildCreateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create")] + public async Task BuildCreate_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + await GetLoggedInCli() + .Command(k_BuildCreateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create")] + public async Task BuildCreate_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command(k_BuildCreateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildCreateVersionTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildCreateVersionTests.cs new file mode 100644 index 0000000..6ff9c39 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildCreateVersionTests.cs @@ -0,0 +1,113 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + const string k_BuildCreateVersionCommand = "gsh build create-version"; + const string k_BuildCreateVersionBucketArgs = " 101 --access-key test-access-key --secret-key test-secret-key --bucket-url test-bucket-url"; + const string k_BuildCreateVersionContainerArgs = " 102 --tag v1"; + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create-version")] + public async Task BuildCreateVersion_Bucket_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(k_BuildCreateVersionCommand + k_BuildCreateVersionBucketArgs) + .AssertStandardOutputContains("Creating build version...") + .AssertStandardErrorContains("Build version created successfully") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create-version")] + public async Task BuildCreateVersion_Container_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(k_BuildCreateVersionCommand + k_BuildCreateVersionContainerArgs) + .AssertStandardOutputContains("Creating build version...") + .AssertStandardErrorContains("Build version created successfully") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create-version")] + public async Task BuildCreateVersion_FileUpload_SucceedsWithValidInput() + { + var args = " 103 --directory " + m_TempDirectoryPath; + await GetFullySetCli() + .Command(k_BuildCreateVersionCommand + args) + .AssertStandardOutputContains("Creating build version...") + .AssertStandardErrorContains("Build version created successfully") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create-version")] + public async Task BuildCreateVersion_ThrowsMissingIdException() + { + await GetFullySetCli() + .Command("gsh build create-version") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Required argument missing for command: 'create-version'.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create-version")] + public async Task BuildCreateVersion_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_BuildCreateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create-version")] + public async Task BuildCreateVersion_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + await GetLoggedInCli() + .Command(k_BuildCreateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build create-version")] + public async Task BuildCreateVersion_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command(k_BuildCreateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildDeleteTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildDeleteTests.cs new file mode 100644 index 0000000..9fcdee1 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildDeleteTests.cs @@ -0,0 +1,99 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + const string k_BuildDeleteCommand = "gsh build delete 1"; + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build delete")] + public async Task BuildDelete_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(k_BuildDeleteCommand) + .AssertExitCode(ExitCode.Success) + .AssertStandardOutputContains("Deleting build...") + .AssertStandardErrorContains("Build deleted successfully") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build delete")] + public async Task BuildDelete_ThrowsMissingBuildIdException() + { + await GetFullySetCli() + .Command("gsh build delete") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Required argument missing for command: 'delete'.") + .ExecuteAsync(); + } + + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build delete")] + public async Task BuildDelete_ThrowsInvalidBuildIdException() + { + await GetFullySetCli() + .Command("gsh build delete a") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Build ID 'a' not a valid ID.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build delete")] + public async Task BuildDelete_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_BuildDeleteCommand) + .AssertStandardErrorContains(k_NotLoggedIn) + .AssertExitCode(ExitCode.HandledError) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build delete")] + public async Task BuildDelete_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + await GetLoggedInCli() + .Command(k_BuildDeleteCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build delete")] + public async Task BuildDelete_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command(k_BuildDeleteCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildGetTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildGetTests.cs new file mode 100644 index 0000000..e35bd86 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildGetTests.cs @@ -0,0 +1,104 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + const string k_BuildGetCommand = "gsh build get 1"; + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build get")] + public async Task BuildGet_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(k_BuildGetCommand) + .AssertStandardOutput( + // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local + str => + { + Assert.IsTrue(str.Contains("Fetching build...")); + Assert.IsTrue(str.Contains("buildName: name1")); + }) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build get")] + public async Task BuildGet_ThrowsMissingBuildIdException() + { + await GetFullySetCli() + .Command("gsh build get") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Required argument missing for command: 'get'.") + .ExecuteAsync(); + } + + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build get")] + public async Task BuildGet_ThrowsInvalidBuildIdException() + { + await GetFullySetCli() + .Command("gsh build get a") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Build ID 'a' not a valid ID.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build get")] + public async Task BuildGet_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_BuildGetCommand) + .AssertStandardErrorContains(k_NotLoggedIn) + .AssertExitCode(ExitCode.HandledError) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build get")] + public async Task BuildGet_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + await GetLoggedInCli() + .Command(k_BuildGetCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build get")] + public async Task BuildGet_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command(k_BuildGetCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildInstallsTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildInstallsTests.cs new file mode 100644 index 0000000..740d9a1 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildInstallsTests.cs @@ -0,0 +1,104 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + const string k_BuildInstallsCommand = "gsh build installs 1"; + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build installs")] + public async Task BuildInstalls_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(k_BuildInstallsCommand) + .AssertNoErrors() + .AssertStandardOutput( + // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local + str => + { + Assert.IsTrue(str.Contains("Fetching installs...")); + Assert.IsTrue(str.Contains("fleetName: fleet name")); + }) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build installs")] + public async Task BuildInstalls_ThrowsMissingBuildIdException() + { + await GetFullySetCli() + .Command("gsh build installs") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Required argument missing for command: 'installs'.") + .ExecuteAsync(); + } + + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build installs")] + public async Task BuildInstalls_ThrowsInvalidBuildIdException() + { + await GetFullySetCli() + .Command("gsh build installs a") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Build ID 'a' not a valid ID.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build installs")] + public async Task BuildInstalls_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_BuildInstallsCommand) + .AssertStandardErrorContains(k_NotLoggedIn) + .AssertExitCode(ExitCode.HandledError) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build installs")] + public async Task BuildInstalls_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + await GetLoggedInCli() + .Command(k_BuildInstallsCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build installs")] + public async Task BuildInstalls_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command(k_BuildInstallsCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildListTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildListTests.cs new file mode 100644 index 0000000..466777c --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildListTests.cs @@ -0,0 +1,79 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + const string k_BuildListCommand = "gsh build list"; + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build list")] + public async Task BuildList_Succeeds() + { + await GetFullySetCli() + .Command(k_BuildListCommand) + .AssertStandardOutput( + // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local + str => + { + Assert.IsTrue(str.Contains("Fetching build list...")); + Assert.IsTrue(str.Contains("buildName: Build1")); + Assert.IsTrue(str.Contains("buildName: Build2")); + }) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build list")] + public async Task BuildList_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_BuildCreateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build list")] + public async Task BuildList_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + await GetLoggedInCli() + .Command(k_BuildListCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build list")] + public async Task BuildList_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command(k_BuildListCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildUpdateTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildUpdateTests.cs new file mode 100644 index 0000000..b156254 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingBuildUpdateTests.cs @@ -0,0 +1,73 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + const string k_BuildUpdateCommand = "gsh build update 1 --name \"Updated Name\""; + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build update")] + public async Task BuildUpdate_Succeeds() + { + await GetFullySetCli() + .Command(k_BuildUpdateCommand) + .AssertStandardOutputContains("Updating build...") + .AssertStandardErrorContains("Build updated successfully") + .AssertExitCode(ExitCode.Success) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build update")] + public async Task BuildUpdate_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_BuildCreateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build update")] + public async Task BuildUpdate_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + await GetLoggedInCli() + .Command(k_BuildUpdateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh build")] + [Category("gsh build update")] + public async Task BuildUpdate_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command(k_BuildUpdateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetRegionAvailableTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetRegionAvailableTests.cs new file mode 100644 index 0000000..14a1c05 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetRegionAvailableTests.cs @@ -0,0 +1,90 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; +using Unity.Services.Cli.MockServer.ServiceMocks.GameServerHosting; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + internal const string FleetRegionAvailableCommand = $"gsh fleet-region available {Keys.ValidFleetId}"; + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region available")] + public async Task FleetRegionAvailable_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(FleetRegionAvailableCommand) + .AssertStandardOutput( + v => + { + StringAssert.Contains("Fetching available regions...", v); + StringAssert.Contains("name: US East",v); + }) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region available")] + public async Task FleetRegionAvailable_ThrowsInvalidFleetException() + { + await GetFullySetCli() + .Command($"gsh fleet-region available A") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Fleet 'A' not a valid UUID.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region available")] + public async Task FleetRegionAvailable_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(FleetRegionAvailableCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region available")] + public async Task FleetRegionAvailable_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + await GetLoggedInCli() + .Command(FleetRegionAvailableCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region available")] + public async Task FleetRegionAvailable_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command(FleetRegionAvailableCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetRegionCreateTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetRegionCreateTests.cs new file mode 100644 index 0000000..77c0ce8 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetRegionCreateTests.cs @@ -0,0 +1,185 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; +using Unity.Services.Cli.MockServer.ServiceMocks.GameServerHosting; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + internal const string FleetRegionCreateCommand = + $"gsh fleet-region create --fleet-id {Keys.ValidFleetId} --region-id {Keys.ValidRegionId} --min-available-servers 1 --max-servers 2"; + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region create")] + public async Task FleetRegionCreate_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(FleetRegionCreateCommand) + .AssertStandardOutput( + v => + { + StringAssert.Contains("Creating fleet region...", v); + StringAssert.Contains("fleetRegionId: 00000000-0000-0000-0000-200000000000",v); + }) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region create")] + public async Task FleetRegionCreate_ThrowsMissingFleetException() + { + await GetFullySetCli() + .Command($"gsh fleet-region create --region-id {Keys.ValidRegionId} --min-available-servers 1 --max-servers 2") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--fleet-id' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region create")] + public async Task FleetRegionCreate_ThrowsMissingRegionException() + { + await GetFullySetCli() + .Command($"gsh fleet-region create --fleet-id {Keys.ValidFleetId} --min-available-servers 1 --max-servers 2") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--region-id' is required.") + .ExecuteAsync(); + } + + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region create")] + public async Task FleetRegionCreate_ThrowsMissingMinServersException() + { + await GetFullySetCli() + .Command($"gsh fleet-region create --fleet-id {Keys.ValidFleetId} --region-id {Keys.ValidRegionId} --max-servers 2") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--min-available-servers' is required.") + .ExecuteAsync(); + } + + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region create")] + public async Task FleetRegionCreate_ThrowsMissingMaxServersException() + { + await GetFullySetCli() + .Command($"gsh fleet-region create --fleet-id {Keys.ValidFleetId} --region-id {Keys.ValidRegionId} --min-available-servers 1") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--max-servers' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region create")] + public async Task FleetRegionCreate_ThrowsInvalidFleetException() + { + await GetFullySetCli() + .Command($"gsh fleet-region create --fleet-id invalid --region-id {Keys.ValidRegionId} --min-available-servers 1 --max-servers 2") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Cannot parse argument 'invalid' for option '--fleet-id'") + .ExecuteAsync(); + } + + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region create")] + public async Task FleetRegionCreate_ThrowsInvalidRegionException() + { + await GetFullySetCli() + .Command($"gsh fleet-region create --fleet-id {Keys.ValidFleetId} --region-id invalid --min-available-servers 1 --max-servers 2") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Cannot parse argument 'invalid' for option '--region-id'") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region create")] + public async Task FleetRegionCreate_ThrowsInvalidMinAvailableException() + { + await GetFullySetCli() + .Command($"gsh fleet-region create --fleet-id {Keys.ValidFleetId} --region-id {Keys.ValidRegionId} --min-available-servers ABC --max-servers 2") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Cannot parse argument 'ABC' for option '--min-available-servers'") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region create")] + public async Task FleetRegionCreate_ThrowsInvalidMaxServersException() + { + await GetFullySetCli() + .Command($"gsh fleet-region create --fleet-id {Keys.ValidFleetId} --region-id {Keys.ValidRegionId} --min-available-servers 1 --max-servers ABC") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Cannot parse argument 'ABC' for option '--max-servers'") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region create")] + public async Task FleetRegionCreate_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(FleetRegionCreateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region create")] + public async Task FleetRegionCreate_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + await GetLoggedInCli() + .Command(FleetRegionCreateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region create")] + public async Task FleetRegionCreate_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command(FleetRegionCreateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetRegionTemplatesTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetRegionTemplatesTests.cs new file mode 100644 index 0000000..45a1096 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetRegionTemplatesTests.cs @@ -0,0 +1,77 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; +using Unity.Services.Cli.MockServer.ServiceMocks.GameServerHosting; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + internal const string FleetRegionTemplatesCommand = $"gsh fleet-region templates"; + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region templates")] + public async Task FleetRegionTemplates_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(FleetRegionTemplatesCommand) + .AssertStandardOutput( + v => + { + StringAssert.Contains("Fetching region list...", v); + StringAssert.Contains("name: Example Region",v); + }) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region templates")] + public async Task FleetRegionTemplates_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(FleetRegionTemplatesCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region templates")] + public async Task FleetRegionTemplates_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + await GetLoggedInCli() + .Command(FleetRegionTemplatesCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet region")] + [Category("gsh fleet region templates")] + public async Task FleetRegionTemplates_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command(FleetRegionTemplatesCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetTests.cs new file mode 100644 index 0000000..a9154d5 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingFleetTests.cs @@ -0,0 +1,512 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; +using Unity.Services.Cli.MockServer.ServiceMocks.GameServerHosting; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + static readonly string k_FleetCreateCommand = + $"gsh fleet create --name test --os-family linux --region-id {Keys.ValidTemplateRegionId} --build-configuration-id {Keys.ValidBuildConfigurationId}"; + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet create")] + public async Task FleetCreate_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_FleetCreateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet create")] + public async Task FleetCreate_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await GetLoggedInCli() + .Command(k_FleetCreateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet create")] + public async Task FleetCreate_ThrowsEnvironmentNameNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + + await GetLoggedInCli() + .Command(k_FleetCreateCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet create")] + public async Task FleetCreate_ThrowsBuildConfigurationIdNotSetException() + { + await GetFullySetCli() + .Command($"gsh fleet create --name test --os-family linux --region-id {Keys.ValidTemplateRegionId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--build-configuration-id' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet create")] + public async Task FleetCreate_ThrowsRegionIdNotSetException() + { + await GetFullySetCli() + .Command( + $"gsh fleet create --name test --os-family linux --build-configuration-id {Keys.ValidBuildConfigurationId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--region-id' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet create")] + public async Task FleetCreate_ThrowsOsFamilyNotSetException() + { + await GetFullySetCli() + .Command( + $"gsh fleet create --name test --region-id {Keys.ValidTemplateRegionId} --build-configuration-id {Keys.ValidBuildConfigurationId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--os-family' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet create")] + public async Task FleetCreate_ThrowsNameNotSetException() + { + await GetFullySetCli() + .Command( + $"gsh fleet create --os-family linux --region-id {Keys.ValidTemplateRegionId} --build-configuration-id {Keys.ValidBuildConfigurationId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Option '--name' is required.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet create")] + public async Task FleetCreate_ThrowsInvalidBuildConfigurationIdException() + { + await GetFullySetCli() + .Command( + $"gsh fleet create --name test --os-family linux --region-id {Keys.ValidTemplateRegionId} --build-configuration-id invalid") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains( + "Cannot parse argument 'invalid' for option '--build-configuration-id' as expected type 'System.Int64'.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet create")] + public async Task FleetCreate_ThrowsInvalidRegionIdException() + { + await GetFullySetCli() + .Command( + $"gsh fleet create --name test --os-family linux --region-id invalid --build-configuration-id {Keys.ValidBuildConfigurationId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Region 'invalid' not a valid UUID.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet create")] + public async Task FleetCreate_ThrowsInvalidOsFamilyException() + { + await GetFullySetCli() + .Command( + $"gsh fleet create --name test --os-family invalid --region-id {Keys.ValidTemplateRegionId} --build-configuration-id {Keys.ValidBuildConfigurationId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Invalid option for --os-family.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet create")] + public async Task FleetCreate_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(k_FleetCreateCommand) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet delete")] + public async Task FleetDelete_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command($"gsh fleet delete {Keys.ValidFleetId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet delete")] + public async Task FleetDelete_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await GetLoggedInCli() + .Command($"gsh fleet delete {Keys.ValidFleetId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet delete")] + public async Task FleetDelete_ThrowsEnvironmentNameNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + + await GetLoggedInCli() + .Command($"gsh fleet delete {Keys.ValidFleetId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet delete")] + public async Task FleetDelete_ThrowsFleetIdNotValidException() + { + await GetFullySetCli() + .Command("gsh fleet delete invalid-fleet-id") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Fleet 'invalid-fleet-id' not a valid UUID.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet delete")] + public async Task FleetDelete_ThrowsFleetIdNotSetException() + { + await GetFullySetCli() + .Command("gsh fleet delete") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Required argument missing for command: 'delete'.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet delete")] + public async Task FleetDelete_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command($"gsh fleet delete {Keys.ValidFleetId}") + .AssertExitCode(ExitCode.Success) + .AssertStandardOutputContains("Deleting fleet...") + .AssertStandardErrorContains("Fleet deleted successfully") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet get")] + public async Task FleetGet_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command($"gsh fleet get {Keys.ValidFleetId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet get")] + public async Task FleetGet_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await GetLoggedInCli() + .Command($"gsh fleet get {Keys.ValidFleetId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet get")] + public async Task FleetGet_ThrowsEnvironmentNameNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + + await GetLoggedInCli() + .Command($"gsh fleet get {Keys.ValidFleetId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet get")] + public async Task FleetGet_ThrowsFleetIdNotValidException() + { + await GetFullySetCli() + .Command("gsh fleet get invalid-fleet-id") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Fleet 'invalid-fleet-id' not a valid UUID.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet get")] + public async Task FleetGet_ThrowsFleetIdNotSetException() + { + await GetFullySetCli() + .Command("gsh fleet get") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Required argument missing for command: 'get'.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet get")] + public async Task FleetGet_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command($"gsh fleet get {Keys.ValidFleetId}") + .AssertNoErrors() + .AssertStandardOutputContains("name: Test Fleet") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet list")] + public async Task FleetList_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command("gsh fleet list") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet list")] + public async Task FleetList_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await GetLoggedInCli() + .Command("gsh fleet list") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet list")] + public async Task FleetList_ThrowsEnvironmentNameNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + + await GetLoggedInCli() + .Command("gsh fleet list") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet list")] + public async Task FleetList_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command("gsh fleet list") + .AssertNoErrors() + .AssertStandardOutputContains("Fetching fleet list...") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet update")] + public async Task FleetUpdate_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command($"gsh fleet update {Keys.ValidFleetId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet update")] + public async Task FleetUpdate_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await GetLoggedInCli() + .Command($"gsh fleet update {Keys.ValidFleetId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet update")] + public async Task FleetUpdate_ThrowsEnvironmentNameNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + + await GetLoggedInCli() + .Command($"gsh fleet update {Keys.ValidFleetId}") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet update")] + public async Task FleetUpdate_ThrowsFleetIdNotValidException() + { + await GetFullySetCli() + .Command("gsh fleet update invalid-fleet-id") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Fleet 'invalid-fleet-id' not a valid UUID.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet update")] + public async Task FleetUpdate_ThrowsFleetIdNotSetException() + { + await GetFullySetCli() + .Command("gsh fleet update") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Required argument missing for command: 'update'.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet update")] + public async Task FleetUpdate_ThrowsInvalidBuildConfigurationIdException() + { + await GetFullySetCli() + .Command($"gsh fleet update {Keys.ValidFleetId} --build-configurations invalid") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains( + "Cannot parse argument 'invalid' for option '--build-configurations' as expected type 'System.Int64'.") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh fleet")] + [Category("gsh fleet update")] + public async Task FleetUpdate_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command($"gsh fleet update {Keys.ValidFleetId} --name updated") + .AssertExitCode(ExitCode.Success) + .AssertStandardOutputContains("Updating fleet...") + .AssertStandardErrorContains("Fleet updated successfully") + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingServerGetTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingServerGetTests.cs new file mode 100644 index 0000000..2f8e776 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingServerGetTests.cs @@ -0,0 +1,105 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; +using Unity.Services.Cli.MockServer.ServiceMocks.GameServerHosting; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + static readonly string k_ServersGetCommand = $"gsh server get {Keys.ValidServerId}"; + + [Test] + [Category("gsh")] + [Category("gsh server")] + [Category("gsh server get")] + public async Task ServerGet_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(k_ServersGetCommand) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh server")] + [Category("gsh server get")] + public async Task ServerGet_Succeeds() + { + await GetFullySetCli() + .Command(k_ServersGetCommand) + .AssertStandardOutput( + // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local + str => + { + Assert.IsTrue(str.Contains("Fetching server...")); + Assert.IsTrue(str.Contains($"id: {Keys.ValidServerId}")); + }) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh server")] + [Category("gsh server get")] + public async Task ServerGet_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_ServersGetCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh server")] + [Category("gsh server get")] + public async Task ServerGet_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await GetLoggedInCli() + .Command(k_ServersGetCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh server")] + [Category("gsh server get")] + public async Task ServerGet_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command(k_ServersGetCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh server")] + [Category("gsh server get")] + public async Task ServerGet_ThrowsServerIdNotValidException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command("gsh server get invalid-server-id") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Server ID 'invalid-server-id' not a valid ID") + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingServerListTest.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingServerListTest.cs new file mode 100644 index 0000000..3565a89 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingServerListTest.cs @@ -0,0 +1,121 @@ +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.Common.Exceptions; +using Unity.Services.Cli.IntegrationTest.Common; +using Unity.Services.Cli.MockServer.Common; +using Unity.Services.Cli.MockServer.ServiceMocks.GameServerHosting; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests +{ + static readonly string k_ServerListCommand = $"gsh server list --fleet-id {Keys.ValidFleetId}"; + + [Test] + [Category("gsh")] + [Category("gsh server")] + [Category("gsh server list")] + public async Task ServerList_SucceedsWithValidInput() + { + await GetFullySetCli() + .Command(k_ServerListCommand) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh server")] + [Category("gsh server list")] + public async Task ServerList_Succeeds() + { + await GetFullySetCli() + .Command(k_ServerListCommand) + .AssertStandardOutput( + // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local + str => + { + Assert.IsTrue(str.Contains("Fetching server list...")); + Assert.IsTrue(str.Contains("id: 123")); + }) + .AssertNoErrors() + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh server")] + [Category("gsh server list")] + public async Task ServerList_ThrowsNotLoggedInException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await new UgsCliTestCase() + .Command(k_ServerListCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_NotLoggedIn) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh server")] + [Category("gsh server list")] + public async Task ServerList_ThrowsProjectIdNotSetException() + { + SetConfigValue("environment-id", CommonKeys.ValidEnvironmentId); + SetConfigValue("environment-name", CommonKeys.ValidEnvironmentName); + + await GetLoggedInCli() + .Command(k_ServerListCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_ProjectIdIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh server")] + [Category("gsh server list")] + public async Task ServerList_ThrowsEnvironmentIdNotSetException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command(k_ServerListCommand) + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains(k_EnvironmentNameIsNotSet) + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh server")] + [Category("gsh server list")] + public async Task ServerList_ThrowsFleetIdNotValidException() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command("gsh server list --fleet-id invalid-fleet-id") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains("Invalid option for --fleet-id. invalid-fleet-id is not a valid UUID") + .ExecuteAsync(); + } + + [Test] + [Category("gsh")] + [Category("gsh server")] + [Category("gsh server list")] + public async Task ServerList_ThrowsBuildConfigurationIdNotValid() + { + SetConfigValue("project-id", CommonKeys.ValidProjectId); + await GetLoggedInCli() + .Command( + $"gsh server list --fleet-id {Keys.ValidFleetId} --build-configuration-id invalid-build-configuration-id") + .AssertExitCode(ExitCode.HandledError) + .AssertStandardErrorContains( + "Invalid option for --build-configuration-id. invalid-build-configuration-id is not a valid number.") + .ExecuteAsync(); + } +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingTests.cs b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingTests.cs new file mode 100644 index 0000000..01c73e7 --- /dev/null +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/GameServerHostingTests/GameServerHostingTests.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using NUnit.Framework; +using Unity.Services.Cli.MockServer.ServiceMocks; +using Unity.Services.Cli.MockServer.ServiceMocks.GameServerHosting; + +namespace Unity.Services.Cli.IntegrationTest.GameServerHostingTests; + +public partial class GameServerHostingTests : UgsCliFixture +{ + [OneTimeSetUp] + public async Task OneTimeSetUp() + { + m_MockApi.Server?.AllowPartialMapping(); + await m_MockApi.MockServiceAsync(new IdentityV1Mock()); + await m_MockApi.MockServiceAsync(new GameServerHostingApiMock()); + CreateTempFiles(); + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + m_MockApi.Server?.Dispose(); + m_MockApi.Server?.ResetMappings(); + DeleteTempFiles(); + } + + [SetUp] + public void SetUp() + { + DeleteLocalConfig(); + DeleteLocalCredentials(); + } + + void CreateTempFiles() + { + m_TempDirectoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(m_TempDirectoryPath); + File.WriteAllText(TempFilePath, "file content to upload"); + } + + void DeleteTempFiles() + { + if (m_TempDirectoryPath != null && Directory.Exists(m_TempDirectoryPath)) + { + Directory.Delete(m_TempDirectoryPath, true); + } + } + + string? m_TempDirectoryPath; + string TempFilePath => Path.Combine(m_TempDirectoryPath ?? "", TempFileName); + static string TempFileName => GameServerHostingApiMock.TempFileName; + + const string k_NotLoggedIn = "You are not logged into any service account."; + const string k_ProjectIdIsNotSet = "'project-id' is not set in project configuration."; + const string k_EnvironmentNameIsNotSet = "'environment-name' is not set in project configuration."; + + const string k_BuildConfigurationCreateOrUpdateCommandComplete = "--binary-path simple-game-server-go --build 25289 --command-line \"--init game.init\" --cores 1 --memory 100 --name \"Testing BC\" --query-type sqp --speed 100"; + const string k_BuildConfigurationCreateOrUpdateCommandMissingBinaryPath = "--build 25289 --command-line \"--init game.init\" --cores 1 --memory 100 --name \"Testing BC\" --query-type sqp --speed 100"; + const string k_BuildConfigurationCreateOrUpdateCommandMissingBuild = "--binary-path simple-game-server-go --command-line \"--init game.init\" --cores 1 --memory 100 --name \"Testing BC\" --query-type sqp --speed 100"; + const string k_BuildConfigurationCreateOrUpdateCommandMissingCommandLine = "--binary-path simple-game-server-go --build 25289 --cores 1 --memory 100 --name \"Testing BC\" --query-type sqp --speed 100"; + const string k_BuildConfigurationCreateOrUpdateCommandMissingCores = "--binary-path simple-game-server-go --build 25289 --command-line \"--init game.init\" --memory 100 --name \"Testing BC\" --query-type sqp --speed 100"; + const string k_BuildConfigurationCreateOrUpdateCommandMissingMemory = "--binary-path simple-game-server-go --build 25289 --command-line \"--init game.init\" --cores 1 --name \"Testing BC\" --query-type sqp --speed 100"; + const string k_BuildConfigurationCreateOrUpdateCommandMissingName = "--binary-path simple-game-server-go --build 25289 --command-line \"--init game.init\" --cores 1 --memory 100 --query-type sqp --speed 100"; + const string k_BuildConfigurationCreateOrUpdateCommandMissingQueryType = "--binary-path simple-game-server-go --build 25289 --command-line \"--init game.init\" --cores 1 --memory 100 --name \"Testing BC\" --speed 100"; + const string k_BuildConfigurationCreateOrUpdateCommandMissingSpeed = "--binary-path simple-game-server-go --build 25289 --command-line \"--init game.init\" --cores 1 --memory 100 --name \"Testing BC\" --query-type sqp"; +} diff --git a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/Unity.Services.Cli.IntegrationTest.csproj b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/Unity.Services.Cli.IntegrationTest.csproj index 0f00004..c35715f 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/Unity.Services.Cli.IntegrationTest.csproj +++ b/Unity.Services.Cli/Unity.Services.Cli.IntegrationTest/Unity.Services.Cli.IntegrationTest.csproj @@ -24,6 +24,7 @@ + diff --git a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/CreateLeaderboardHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/CreateLeaderboardHandlerTests.cs index 1b498cf..10f5a09 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/CreateLeaderboardHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/CreateLeaderboardHandlerTests.cs @@ -69,7 +69,7 @@ public async Task CreateHandler_CallsCreateServiceSucceeded() "{}", CancellationToken.None)).ReturnsAsync(new ApiResponse(HttpStatusCode.Created, new object())); - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await CreateLeaderboardHandler.CreateAsync( @@ -79,7 +79,7 @@ await CreateLeaderboardHandler.CreateAsync( m_MockLogger.Object, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockLeaderboard.Verify( e => e.CreateLeaderboardAsync( TestValues.ValidProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/DeleteLeaderboardHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/DeleteLeaderboardHandlerTests.cs index 3dd674c..e2a1780 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/DeleteLeaderboardHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/DeleteLeaderboardHandlerTests.cs @@ -59,7 +59,7 @@ public async Task DeleteHandler_CallsDeleteServiceSucceeded() leaderboardId, CancellationToken.None)).ReturnsAsync(new ApiResponse(HttpStatusCode.NoContent, new object())); - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await DeleteLeaderboardHandler.DeleteAsync( @@ -69,7 +69,7 @@ await DeleteLeaderboardHandler.DeleteAsync( m_MockLogger.Object, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockLeaderboard.Verify( e => e.DeleteLeaderboardAsync( TestValues.ValidProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/ExportHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/ExportHandlerTests.cs index e5f650a..a57485b 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/ExportHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/ExportHandlerTests.cs @@ -80,7 +80,7 @@ public async Task ExportAsync_ExportsAndZips() Cursor = TestValues.Cursor, Limit = TestValues.Limit }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); m_MockLeaderboardsService.Setup( @@ -142,7 +142,7 @@ public async Task ExportAsync_ExportsAndZipsWithFileName() Cursor = TestValues.Cursor, Limit = TestValues.Limit }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); m_MockLeaderboardsService.Setup( @@ -205,7 +205,7 @@ public void ExportAsync_FailsWithInvalidExtension() Cursor = TestValues.Cursor, Limit = TestValues.Limit }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); m_LeaderboardExporter!.ListLeaderboardInput = input; diff --git a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/GetLeaderboardConfigsHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/GetLeaderboardConfigsHandlerTests.cs index 759294a..693e14d 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/GetLeaderboardConfigsHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/GetLeaderboardConfigsHandlerTests.cs @@ -49,7 +49,7 @@ public async Task ListHandler_CallsListServiceAndLoggerWhenInputIsValid() Cursor = TestValues.Cursor, Limit = TestValues.Limit }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await GetLeaderboardConfigsHandler.GetLeaderboardConfigsAsync( @@ -60,7 +60,7 @@ await GetLeaderboardConfigsHandler.GetLeaderboardConfigsAsync( CancellationToken.None ); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockLeaderboard.Verify( ex => ex.GetLeaderboardsAsync(TestValues.ValidProjectId, TestValues.ValidEnvironmentId, TestValues.Cursor, TestValues.Limit, CancellationToken.None), Times.Once); diff --git a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/GetLeaderboardHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/GetLeaderboardHandlerTests.cs index 4224c8b..1f9ce05 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/GetLeaderboardHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/GetLeaderboardHandlerTests.cs @@ -43,7 +43,7 @@ public void SetUp() updateType: UpdateType.Aggregate, bucketSize: Decimal.One, resetConfig: new ResetConfig(), - tieringConfig: new TieringConfig(TieringConfig.StrategyEnum.Percent, new List(){new TieringConfigTiersInner("tier1", 2)}), + tieringConfig: new TieringConfig(TieringConfig.StrategyEnum.Percent, new List() { new TieringConfigTiersInner("tier1", 2) }), updated: DateTime.Today, created: DateTime.Now, lastReset: DateTime.Today, @@ -71,7 +71,7 @@ public async Task GetHandler_CallsGetServiceAndLogger() LeaderboardId = k_LeaderboardId }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await GetLeaderboardHandler.GetLeaderboardConfigAsync( @@ -81,7 +81,7 @@ await GetLeaderboardHandler.GetLeaderboardConfigAsync( m_MockLogger.Object, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockLeaderboard.Verify( e => e.GetLeaderboardAsync( TestValues.ValidProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/ImportHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/ImportHandlerTests.cs index 3a5dc58..d7e2cfc 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/ImportHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/ImportHandlerTests.cs @@ -92,7 +92,7 @@ public async Task ImportAsync_Unzips() DryRun = true }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await ImportInternalAsync(importInput); @@ -117,7 +117,7 @@ public async Task ImportAsync_UnzipsWithFileName() DryRun = true }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await ImportInternalAsync(importInput); @@ -142,7 +142,7 @@ public void ImportAsync_FailsWithInvalidExtension() DryRun = true }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); var exception = Assert.ThrowsAsync(async () => @@ -161,7 +161,7 @@ public async Task ImportAsync_DryRunDoesNotImport() DryRun = true }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await ImportInternalAsync(importInput); @@ -180,7 +180,7 @@ public async Task ConfigExists_Updates() InputDirectory = "mock_input_directory", }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); SetupList(m_MockConfigs); SetupUpdate(); @@ -202,7 +202,7 @@ public async Task ConfigDoesNotExist_Creates() InputDirectory = "mock_input_directory", }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); SetupGet(null!, true); SetupCreate(); @@ -225,7 +225,7 @@ public async Task Reconcile_Deletes() Reconcile = true }; - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); SetupList(new List() { @@ -233,7 +233,7 @@ public async Task Reconcile_Deletes() "remote_name") }); SetupDelete(); - SetupList(new List() {new ("to_delete", "to_delete")}); + SetupList(new List() { new("to_delete", "to_delete") }); SetupCreate(); await ImportInternalAsync(importInput); diff --git a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/ResetLeaderboardHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/ResetLeaderboardHandlerTests.cs index a4bf983..801235d 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/ResetLeaderboardHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/ResetLeaderboardHandlerTests.cs @@ -61,7 +61,7 @@ public async Task ResetHandler_CallsResetServiceAndLogger() null, CancellationToken.None)).ReturnsAsync(new ApiResponse(HttpStatusCode.OK, new LeaderboardVersionId(), "{ \"versionId\": 10 }")); - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await ResetLeaderboardHandler.ResetAsync( @@ -71,7 +71,7 @@ await ResetLeaderboardHandler.ResetAsync( m_MockLogger.Object, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockLeaderboard.Verify( e => e.ResetLeaderboardAsync( TestValues.ValidProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/UpdateLeaderboardHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/UpdateLeaderboardHandlerTests.cs index 23faedf..d6b3b69 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/UpdateLeaderboardHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards.UnitTest/Handlers/UpdateLeaderboardHandlerTests.cs @@ -70,7 +70,7 @@ public async Task UpdateHandler_CallsUpdateServiceSucceeded() "{}", CancellationToken.None)).ReturnsAsync(new ApiResponse(HttpStatusCode.NoContent, new object())); - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestValues.ValidEnvironmentId); await UpdateLeaderboardHandler.UpdateAsync( @@ -80,7 +80,7 @@ await UpdateLeaderboardHandler.UpdateAsync( m_MockLogger.Object, CancellationToken.None); - m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(), Times.Once); + m_MockUnityEnvironment.Verify(x => x.FetchIdentifierAsync(CancellationToken.None), Times.Once); m_MockLeaderboard.Verify( e => e.UpdateLeaderboardAsync( TestValues.ValidProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/CreateLeaderboardHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/CreateLeaderboardHandler.cs index 0f5f6e6..f52feb7 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/CreateLeaderboardHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/CreateLeaderboardHandler.cs @@ -21,7 +21,7 @@ internal static async Task CreateAsync( CreateInput input, IUnityEnvironment unityEnvironment, ILeaderboardsService service, ILogger logger, CancellationToken cancellationToken) { - string environmentId = await unityEnvironment.FetchIdentifierAsync(); + string environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); await service.CreateLeaderboardAsync( input.CloudProjectId!, environmentId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/DeleteLeaderboardHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/DeleteLeaderboardHandler.cs index 7343fd4..8f1b5af 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/DeleteLeaderboardHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/DeleteLeaderboardHandler.cs @@ -25,7 +25,7 @@ internal static async Task DeleteAsync( LeaderboardIdInput input, IUnityEnvironment unityEnvironment, ILeaderboardsService service, ILogger logger, CancellationToken cancellationToken) { - string environmentId = await unityEnvironment.FetchIdentifierAsync(); + string environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); await service.DeleteLeaderboardAsync( input.CloudProjectId!, environmentId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/GetLeaderboardConfigsHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/GetLeaderboardConfigsHandler.cs index acbe64a..46ac41e 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/GetLeaderboardConfigsHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/GetLeaderboardConfigsHandler.cs @@ -26,14 +26,14 @@ await loadingIndicator.StartLoadingAsync( _ => GetLeaderboardConfigsAsync(input, unityEnvironment, leaderboardsService, logger, cancellationToken)); } - internal static async Task GetLeaderboardConfigsAsync( + internal static async Task GetLeaderboardConfigsAsync( ListLeaderboardInput input, IUnityEnvironment unityEnvironment, ILeaderboardsService leaderboardsService, ILogger logger, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; var leaderboards = await leaderboardsService.GetLeaderboardsAsync( projectId, environmentId, input.Cursor, input.Limit, cancellationToken); diff --git a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/GetLeaderboardHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/GetLeaderboardHandler.cs index 1d0e165..e62e518 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/GetLeaderboardHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/GetLeaderboardHandler.cs @@ -30,7 +30,7 @@ internal static async Task GetLeaderboardConfigAsync( ILogger logger, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var projectId = input.CloudProjectId!; var response = await leaderboardsService.GetLeaderboardAsync( projectId, environmentId, input.LeaderboardId!, cancellationToken); diff --git a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/ResetLeaderboardHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/ResetLeaderboardHandler.cs index 84c93a7..2031c53 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/ResetLeaderboardHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/ResetLeaderboardHandler.cs @@ -23,7 +23,7 @@ internal static async Task ResetAsync( ResetInput input, IUnityEnvironment unityEnvironment, ILeaderboardsService service, ILogger logger, CancellationToken cancellationToken) { - string environmentId = await unityEnvironment.FetchIdentifierAsync(); + string environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var response = await service.ResetLeaderboardAsync( input.CloudProjectId!, environmentId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/UpdateLeaderboardHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/UpdateLeaderboardHandler.cs index be20054..3691b95 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/UpdateLeaderboardHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Leaderboards/Handlers/UpdateLeaderboardHandler.cs @@ -21,7 +21,7 @@ internal static async Task UpdateAsync( UpdateInput input, IUnityEnvironment unityEnvironment, ILeaderboardsService service, ILogger logger, CancellationToken cancellationToken) { - string environmentId = await unityEnvironment.FetchIdentifierAsync(); + string environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); await service.UpdateLeaderboardAsync( input.CloudProjectId!, environmentId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby.UnitTest/Handlers/ExportHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby.UnitTest/Handlers/ExportHandlerTests.cs index ae81ea5..b326db2 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby.UnitTest/Handlers/ExportHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby.UnitTest/Handlers/ExportHandlerTests.cs @@ -38,7 +38,7 @@ class ExportHandlerTests public void SetUp() { m_MockUnityEnvironment.Reset(); - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(Guid.NewGuid().ToString()); m_MockRemoteConfigService.Reset(); @@ -51,7 +51,8 @@ public void SetUp() It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(() => { + .ReturnsAsync(() => + { return JsonConvert.SerializeObject(new RemoteConfigResponse { Configs = new List{ diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby.UnitTest/Handlers/ImportHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby.UnitTest/Handlers/ImportHandlerTests.cs index cc0676e..4df0899 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby.UnitTest/Handlers/ImportHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby.UnitTest/Handlers/ImportHandlerTests.cs @@ -38,7 +38,7 @@ public void SetUp() m_MockUnityEnvironment.Reset(); m_MockRemoteConfigService.Reset(); - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(Guid.NewGuid().ToString()); m_MockLogger.Reset(); @@ -49,7 +49,7 @@ public void SetUp() It.IsAny(), It.IsAny(), It.IsAny())) - .Returns(Task.FromResult>(new List{ m_Config })); + .Returns(Task.FromResult>(new List { m_Config })); m_LobbyImporter = new LobbyImporter( m_MockRemoteConfigService.Object, @@ -266,7 +266,8 @@ void SetupExistingConfig(LobbyConfig config) It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(() => { + .ReturnsAsync(() => + { return JsonConvert.SerializeObject(new RemoteConfigResponse { Configs = new List{ diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/BulkUpdateLobbyHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/BulkUpdateLobbyHandler.cs index e271f20..5d49017 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/BulkUpdateLobbyHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/BulkUpdateLobbyHandler.cs @@ -14,7 +14,7 @@ static class BulkUpdateLobbyHandler /// public static async Task BulkUpdateLobbyAsync(RequiredBodyInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - var environmentId = await unityEnvironment.FetchIdentifierAsync(); + var environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var response = await service.BulkUpdateLobbyAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/ConfigGetHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/ConfigGetHandler.cs index 8852a50..16e4007 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/ConfigGetHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/ConfigGetHandler.cs @@ -37,7 +37,7 @@ public static async Task ConfigGetAsync(CommonInput input, IUnityEnvironment uni try { - environmentId = await unityEnvironment.FetchIdentifierAsync(); + environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); } catch (MissingConfigurationException) { diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/CreateLobbyHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/CreateLobbyHandler.cs index 01c2ad0..3a3907d 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/CreateLobbyHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/CreateLobbyHandler.cs @@ -14,7 +14,7 @@ static class CreateLobbyHandler /// public static async Task CreateLobbyAsync(RequiredBodyInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - string? environmentId = await unityEnvironment.FetchIdentifierAsync(); + string? environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var response = await service.CreateLobbyAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/DeleteLobbyHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/DeleteLobbyHandler.cs index a75a6a9..4f9a2d6 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/DeleteLobbyHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/DeleteLobbyHandler.cs @@ -13,7 +13,7 @@ static class DeleteLobbyHandler /// public static async Task DeleteLobbyAsync(CommonLobbyInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - string? environmentId = await unityEnvironment.FetchIdentifierAsync(); + string? environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); await service.DeleteLobbyAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/GetHostedLobbiesHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/GetHostedLobbiesHandler.cs index d655728..7bb11b6 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/GetHostedLobbiesHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/GetHostedLobbiesHandler.cs @@ -14,7 +14,7 @@ static class GetHostedLobbiesHandler /// public static async Task GetHostedLobbiesAsync(CommonLobbyInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - string? environmentId = await unityEnvironment.FetchIdentifierAsync(); + string? environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var response = await service.GetHostedLobbiesAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/GetJoinedLobbiesHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/GetJoinedLobbiesHandler.cs index a9c37e6..e34ed14 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/GetJoinedLobbiesHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/GetJoinedLobbiesHandler.cs @@ -14,7 +14,7 @@ static class GetJoinedLobbiesHandler /// public static async Task GetJoinedLobbiesAsync(PlayerInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - string? environmentId = await unityEnvironment.FetchIdentifierAsync(); + string? environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var response = await service.GetJoinedLobbiesAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/GetLobbyHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/GetLobbyHandler.cs index 9250525..1486da4 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/GetLobbyHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/GetLobbyHandler.cs @@ -14,7 +14,7 @@ static class GetLobbyHandler /// public static async Task GetLobbyAsync(CommonLobbyInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - string? environmentId = await unityEnvironment.FetchIdentifierAsync(); + string? environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var response = await service.GetLobbyAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/HeartbeatHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/HeartbeatHandler.cs index 1db8857..587b966 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/HeartbeatHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/HeartbeatHandler.cs @@ -13,7 +13,7 @@ static class HeartbeatHandler /// public static async Task HeartbeatLobbyAsync(CommonLobbyInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - string? environmentId = await unityEnvironment.FetchIdentifierAsync(); + string? environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); await service.HeartbeatLobbyAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/JoinLobbyHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/JoinLobbyHandler.cs index acea609..73967f6 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/JoinLobbyHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/JoinLobbyHandler.cs @@ -14,7 +14,7 @@ static class JoinLobbyHandler /// public static async Task JoinLobbyAsync(JoinInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - string? environmentId = await unityEnvironment.FetchIdentifierAsync(); + string? environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var response = await service.JoinLobbyAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/QueryLobbiesHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/QueryLobbiesHandler.cs index 016e851..cf0a00b 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/QueryLobbiesHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/QueryLobbiesHandler.cs @@ -14,7 +14,7 @@ static class QueryLobbiesHandler /// public static async Task QueryLobbiesAsync(CommonLobbyInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - string? environmentId = await unityEnvironment.FetchIdentifierAsync(); + string? environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var response = await service.QueryLobbiesAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/QuickJoinHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/QuickJoinHandler.cs index 709c373..24b7e11 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/QuickJoinHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/QuickJoinHandler.cs @@ -14,7 +14,7 @@ static class QuickJoinHandler /// public static async Task QuickJoinAsync(CommonLobbyInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - string? environmentId = await unityEnvironment.FetchIdentifierAsync(); + string? environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var response = await service.QuickJoinAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/ReconnectHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/ReconnectHandler.cs index bbe8cc4..675586b 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/ReconnectHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/ReconnectHandler.cs @@ -14,7 +14,7 @@ static class ReconnectHandler /// public static async Task ReconnectAsync(PlayerInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - string? environmentId = await unityEnvironment.FetchIdentifierAsync(); + string? environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var response = await service.ReconnectAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/RemovePlayerHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/RemovePlayerHandler.cs index c2dce99..08ecec6 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/RemovePlayerHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/RemovePlayerHandler.cs @@ -13,7 +13,7 @@ static class RemovePlayerHandler /// public static async Task RemovePlayerAsync(PlayerInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - string? environmentId = await unityEnvironment.FetchIdentifierAsync(); + string? environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); await service.RemovePlayerAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/RequestTokenHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/RequestTokenHandler.cs index 04cbd5a..f26cd10 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/RequestTokenHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/RequestTokenHandler.cs @@ -14,7 +14,7 @@ static class RequestTokenHandler /// public static async Task RequestTokenAsync(LobbyTokenInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - string? environmentId = await unityEnvironment.FetchIdentifierAsync(); + string? environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var response = await service.RequestTokenAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/UpdateLobbyHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/UpdateLobbyHandler.cs index ef4889a..83fbbe6 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/UpdateLobbyHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/UpdateLobbyHandler.cs @@ -14,7 +14,7 @@ static class UpdateLobbyHandler /// public static async Task UpdateLobbyAsync(RequiredBodyInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - string? environmentId = await unityEnvironment.FetchIdentifierAsync(); + string? environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var response = await service.UpdateLobbyAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/UpdatePlayerHandler.cs b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/UpdatePlayerHandler.cs index 3508a67..d4e63f9 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/UpdatePlayerHandler.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.Lobby/Handlers/UpdatePlayerHandler.cs @@ -14,7 +14,7 @@ static class UpdatePlayerHandler /// public static async Task UpdatePlayerAsync(PlayerInput input, IUnityEnvironment unityEnvironment, ILobbyService service, ILogger logger, CancellationToken cancellationToken) { - string? environmentId = await unityEnvironment.FetchIdentifierAsync(); + string? environmentId = await unityEnvironment.FetchIdentifierAsync(cancellationToken); var response = await service.UpdatePlayerAsync( input.CloudProjectId, diff --git a/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig.UnitTest/Deploy/RemoteConfigFetchServiceTests.cs b/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig.UnitTest/Deploy/RemoteConfigFetchServiceTests.cs index 88227b0..2ab73fe 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig.UnitTest/Deploy/RemoteConfigFetchServiceTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig.UnitTest/Deploy/RemoteConfigFetchServiceTests.cs @@ -46,7 +46,7 @@ public class RemoteConfigFetchServiceTests FetchInput m_DefaultInput = new() { - CloudProjectId = k_ValidProjectId , + CloudProjectId = k_ValidProjectId, Reconcile = false }; @@ -71,9 +71,9 @@ public void SetUp() m_MockDeployFileService.Object, m_MockRemoteConfigScriptsLoader.Object); - m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync()) + m_MockUnityEnvironment.Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(k_ValidEnvironmentId); - m_MockDeployFileService.Setup(d => d.ListFilesToDeploy(new [] {m_DefaultInput.Path}, k_DeployFileExtension)) + m_MockDeployFileService.Setup(d => d.ListFilesToDeploy(new[] { m_DefaultInput.Path }, k_DeployFileExtension)) .Returns(k_ValidFilePaths); m_RemoteConfigFiles = new List(k_ValidFilePaths.Count); @@ -159,7 +159,7 @@ public async Task FetchAsync_FailedToLoadIsForwarded() loader.LoadScriptsAsync(It.IsAny>(), CancellationToken.None)) .Returns(Task.FromResult( new LoadResult(Array.Empty(), - new []{ new RemoteConfigFile(failedRcFileName, failedRcFileName) + new[]{ new RemoteConfigFile(failedRcFileName, failedRcFileName) { Status = new DeploymentStatus(Statuses.FailedToRead, string.Empty, SeverityLevel.Error) }}))); diff --git a/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig.UnitTest/ImportExport/ExportHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig.UnitTest/ImportExport/ExportHandlerTests.cs index 059a5b6..55ff660 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig.UnitTest/ImportExport/ExportHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig.UnitTest/ImportExport/ExportHandlerTests.cs @@ -45,7 +45,7 @@ public void SetUp() m_FileSystemMock.Reset(); m_MockUnityEnvironment - .Setup(x => x.FetchIdentifierAsync()) + .Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestEnvironment); m_MockArchiver.Setup( @@ -142,7 +142,7 @@ public void ExportAsync_FailsWithInvalidExtension() [Test] [TestCase(true, true, 1, 0, 0, Description = "DryRun still calls Get")] - [TestCase(false, false, 1, 0 , 0, Description = "Does not create if does not exist")] + [TestCase(false, false, 1, 0, 0, Description = "Does not create if does not exist")] [TestCase(false, true, 1, 0, 0, Description = "Does not update if does not exist")] public async Task ExportAsync_ApiCalls(bool dryRun, bool exists, int getCalls, int updateCalls, int createCalls) { diff --git a/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig.UnitTest/ImportExport/ImportHandlerTests.cs b/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig.UnitTest/ImportExport/ImportHandlerTests.cs index e67bfc6..136686e 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig.UnitTest/ImportExport/ImportHandlerTests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig.UnitTest/ImportExport/ImportHandlerTests.cs @@ -32,7 +32,7 @@ class ImportHandlerTests new RemoteConfigEntryDTO { key = "local", value = true, type = "bool"} }; - ImportInput m_ImportInput = new () + ImportInput m_ImportInput = new() { InputDirectory = "mock_input_directory" }; @@ -46,7 +46,7 @@ public void SetUp() m_MockLogger.Reset(); m_MockUnityEnvironment - .Setup(x => x.FetchIdentifierAsync()) + .Setup(x => x.FetchIdentifierAsync(CancellationToken.None)) .ReturnsAsync(TestEnvironment); m_MockArchiver.Setup( @@ -127,7 +127,7 @@ public void ImportAsync_FailsWithInvalidExtension(bool dryRun) [Test] [TestCase(true, true, 1, 0, 0, Description = "DryRun does not create or update")] - [TestCase(false, false, 1, 0 , 1, Description = "Creates if it does not exist")] + [TestCase(false, false, 1, 0, 1, Description = "Creates if it does not exist")] [TestCase(false, true, 1, 1, 0, Description = "Updates if it exists")] public async Task ImportAsync_ApiCalls(bool dryRun, bool exists, int getCalls, int updateCalls, int createCalls) { diff --git a/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig/Deploy/RemoteConfigFetchService.cs b/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig/Deploy/RemoteConfigFetchService.cs index f29fd63..7e2f60d 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig/Deploy/RemoteConfigFetchService.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig/Deploy/RemoteConfigFetchService.cs @@ -46,9 +46,9 @@ public async Task FetchAsync( StatusContext? loadingContext, CancellationToken cancellationToken) { - var environmentId = await m_UnityEnvironment.FetchIdentifierAsync(); + var environmentId = await m_UnityEnvironment.FetchIdentifierAsync(cancellationToken); m_RemoteConfigClient.Initialize(input.CloudProjectId!, environmentId, cancellationToken); - var remoteConfigFiles = m_DeployFileService.ListFilesToDeploy(new[] {input.Path}, m_DeployFileExtension).ToList(); + var remoteConfigFiles = m_DeployFileService.ListFilesToDeploy(new[] { input.Path }, m_DeployFileExtension).ToList(); var loadResult = await m_RemoteConfigScriptsLoader .LoadScriptsAsync(remoteConfigFiles, cancellationToken); @@ -88,7 +88,7 @@ static RemoteConfigFile ToFetchedFile(IRemoteConfigFile file) static DeployContent GetDeployContent(RemoteConfigEntry entry, string status) { - return new CliRemoteConfigEntry(entry.Key, "RemoteConfig Key", NormalizePath(entry.File) ,100, status, string.Empty); + return new CliRemoteConfigEntry(entry.Key, "RemoteConfig Key", NormalizePath(entry.File), 100, status, string.Empty); } static string NormalizePath(IRemoteConfigFile? file) diff --git a/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig/Unity.Services.Cli.RemoteConfig.csproj b/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig/Unity.Services.Cli.RemoteConfig.csproj index 38bf01d..e4eba9f 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig/Unity.Services.Cli.RemoteConfig.csproj +++ b/Unity.Services.Cli/Unity.Services.Cli.RemoteConfig/Unity.Services.Cli.RemoteConfig.csproj @@ -20,7 +20,7 @@ - + $(DefineConstants);$(ExtraDefineConstants) diff --git a/Unity.Services.Cli/Unity.Services.Cli.ServiceAccountAuthentication.UnitTest/Authenticator/AuthenticatorV1Tests.cs b/Unity.Services.Cli/Unity.Services.Cli.ServiceAccountAuthentication.UnitTest/Authenticator/AuthenticatorV1Tests.cs index ed5069c..50c4ec3 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.ServiceAccountAuthentication.UnitTest/Authenticator/AuthenticatorV1Tests.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.ServiceAccountAuthentication.UnitTest/Authenticator/AuthenticatorV1Tests.cs @@ -54,6 +54,9 @@ public async Task LoginAsyncWithoutArgumentPromptsAndPersistsExpectedToken() .ReturnsAsync(k_ValidServiceKeyId); m_MockPrompt.Setup(p => p.PromptAsync(AuthenticatorV1.SecretKeyPrompt, CancellationToken.None)) .ReturnsAsync(k_ValidServiceSecretKey); + m_MockPrompt.Setup(p => p.IsStandardInputRedirected) + .Returns(false); + var authenticatorV1 = new AuthenticatorV1(m_MockPersister.Object, m_MockPrompt.Object); await authenticatorV1.LoginAsync(input, CancellationToken.None); @@ -61,6 +64,18 @@ public async Task LoginAsyncWithoutArgumentPromptsAndPersistsExpectedToken() m_MockPersister.Verify(p => p.SaveAsync(k_AccessToken, CancellationToken.None), Times.Once); } + [Test] + public void LoginAsyncWithoutArgumentAndRedirectedStandardInputThrows() + { + var input = new LoginInput(); + m_MockPrompt.Setup(p => p.IsStandardInputRedirected) + .Returns(true); + + var authenticatorV1 = new AuthenticatorV1(m_MockPersister.Object, m_MockPrompt.Object); + + Assert.ThrowsAsync(() => authenticatorV1.LoginAsync(input, CancellationToken.None)); + } + [Test] public async Task LoginAsyncWithAllKeyOptionsAndRedirectedInputPersistsExpectedToken() { diff --git a/Unity.Services.Cli/Unity.Services.Cli.ServiceAccountAuthentication/Authenticator/AuthenticatorV1.cs b/Unity.Services.Cli/Unity.Services.Cli.ServiceAccountAuthentication/Authenticator/AuthenticatorV1.cs index 6c814a6..ace6fe9 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.ServiceAccountAuthentication/Authenticator/AuthenticatorV1.cs +++ b/Unity.Services.Cli/Unity.Services.Cli.ServiceAccountAuthentication/Authenticator/AuthenticatorV1.cs @@ -45,6 +45,11 @@ public async Task LoginAsync(LoginInput input, CancellationToken cancellationTok } else { + if (m_CliPrompt.IsStandardInputRedirected) + { + throw new InvalidLoginInputException($"Standard Input is redirected, please use the " + + $"\"{LoginInput.ServiceKeyIdAlias}\" and \"{LoginInput.ServiceSecretKeyAlias}\" options to login."); + } (keyId, secretKey) = await PromptForServiceAccountKeysAsync(m_CliPrompt, cancellationToken); } diff --git a/Unity.Services.Cli/Unity.Services.Cli.sln b/Unity.Services.Cli/Unity.Services.Cli.sln index 5c0189c..4d1ff88 100644 --- a/Unity.Services.Cli/Unity.Services.Cli.sln +++ b/Unity.Services.Cli/Unity.Services.Cli.sln @@ -28,7 +28,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Services.Cli.Lobby", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Services.Cli.Lobby.UnitTest", "Unity.Services.Cli.Lobby.UnitTest\Unity.Services.Cli.Lobby.UnitTest.csproj", "{50E3FBD8-DB4C-4198-B24C-948AD2DAA571}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Services.Cli.GameServerHosting", "Unity.Services.Cli.GameServerHosting\Unity.Services.Cli.GameServerHosting.csproj", "{7FC4167A-B228-45DC-9D12-87EF16BBAAEB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Services.Cli.GameServerHosting.UnitTest", "Unity.Services.Cli.GameServerHosting.UnitTest\Unity.Services.Cli.GameServerHosting.UnitTest.csproj", "{734A360E-FEFC-447A-AD4F-621212EAA3ED}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unity.Services.Cli.Integration.MockServer", "Unity.Services.Cli.Integration.MockServer\Unity.Services.Cli.Integration.MockServer.csproj", "{92DBFB5E-BAD9-4852-9162-DA6B94CE6BF3}" EndProject @@ -127,6 +129,14 @@ Global {50E3FBD8-DB4C-4198-B24C-948AD2DAA571}.Debug|Any CPU.Build.0 = Debug|Any CPU {50E3FBD8-DB4C-4198-B24C-948AD2DAA571}.Release|Any CPU.ActiveCfg = Release|Any CPU {50E3FBD8-DB4C-4198-B24C-948AD2DAA571}.Release|Any CPU.Build.0 = Release|Any CPU + {7FC4167A-B228-45DC-9D12-87EF16BBAAEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7FC4167A-B228-45DC-9D12-87EF16BBAAEB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7FC4167A-B228-45DC-9D12-87EF16BBAAEB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7FC4167A-B228-45DC-9D12-87EF16BBAAEB}.Release|Any CPU.Build.0 = Release|Any CPU + {734A360E-FEFC-447A-AD4F-621212EAA3ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {734A360E-FEFC-447A-AD4F-621212EAA3ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {734A360E-FEFC-447A-AD4F-621212EAA3ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {734A360E-FEFC-447A-AD4F-621212EAA3ED}.Release|Any CPU.Build.0 = Release|Any CPU {92DBFB5E-BAD9-4852-9162-DA6B94CE6BF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {92DBFB5E-BAD9-4852-9162-DA6B94CE6BF3}.Debug|Any CPU.Build.0 = Debug|Any CPU {92DBFB5E-BAD9-4852-9162-DA6B94CE6BF3}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/Unity.Services.Cli/Unity.Services.Cli/Program.cs b/Unity.Services.Cli/Unity.Services.Cli/Program.cs index d1702ee..aeb0571 100644 --- a/Unity.Services.Cli/Unity.Services.Cli/Program.cs +++ b/Unity.Services.Cli/Unity.Services.Cli/Program.cs @@ -28,6 +28,7 @@ using Unity.Services.Cli.Common.Telemetry.AnalyticEvent; using Unity.Services.Cli.Common.Telemetry.AnalyticEvent.AnalyticEventFactory; using Unity.Services.Cli.Authoring; +using Unity.Services.Cli.GameServerHosting; #if FEATURE_LEADERBOARDS using Unity.Services.Cli.Leaderboards; #endif @@ -71,6 +72,7 @@ public static async Task InternalMain(string[] args, Logger logger) host.ConfigureServices(CloudCodeModule.RegisterServices); host.ConfigureServices(RemoteConfigModule.RegisterServices); host.ConfigureServices(AccessModule.RegisterServices); + host.ConfigureServices(GameServerHostingModule.RegisterServices); host.ConfigureServices(LobbyModule.RegisterServices); #if FEATURE_LEADERBOARDS host.ConfigureServices(LeaderboardsModule.RegisterServices); @@ -136,6 +138,7 @@ public static async Task InternalMain(string[] args, Logger logger) .AddModule(new LeaderboardsModule()) #endif .AddModule(new LobbyModule()) + .AddModule(new GameServerHostingModule()) .AddModule(new PlayerModule()) .AddModule(new RemoteConfigModule()) .Build(); @@ -146,7 +149,7 @@ public static async Task InternalMain(string[] args, Logger logger) commandTask => { logger.Write(); - TrySendCommandUsageMetric(analyticEventFactory, parser.Parse(args).CommandResult); + TrySendCommandUsageMetric(analyticEventFactory, parser.Parse(args)); return commandTask.Result; }); } @@ -178,11 +181,18 @@ HelpSectionDelegate WriteSubcommands() => helpContext }; } - static void TrySendCommandUsageMetric(IAnalyticEventFactory analyticEventFactory, SymbolResult symbol) + static void TrySendCommandUsageMetric(IAnalyticEventFactory analyticEventFactory, ParseResult parseResult) { - var command = AnalyticEventUtils.ConvertSymbolResultToString(symbol); + var options = parseResult.Tokens + .Where(t => t.Type == TokenType.Option) + .Select(t => t.ToString()) + .ToArray(); + + var command = AnalyticEventUtils.ConvertSymbolResultToString(parseResult.CommandResult); + var analyticEvent = analyticEventFactory.CreateMetricEvent(); analyticEvent.AddData("command", command); + analyticEvent.AddData("options", options); analyticEvent.AddData("time", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()); analyticEvent.Send(); } diff --git a/Unity.Services.Cli/Unity.Services.Cli/Properties/launchSettings.json b/Unity.Services.Cli/Unity.Services.Cli/Properties/launchSettings.json index 8ab0f67..2a7aa42 100644 --- a/Unity.Services.Cli/Unity.Services.Cli/Properties/launchSettings.json +++ b/Unity.Services.Cli/Unity.Services.Cli/Properties/launchSettings.json @@ -4,7 +4,7 @@ "Unity.Services.Cli": { "commandName": "Project", "workingDirectory": "$(SolutionDir)/../", - "commandLineArgs": "cloud-code modules import Unity.Services.Cli/Unity.Services.Cli.CloudCode.UnitTest/ModuleTestCases test.ccmzip" + "commandLineArgs": "-h" }, "Unity.Services.Cli.PrintTree": { "commandName": "Project", diff --git a/Unity.Services.Cli/Unity.Services.Cli/Unity.Services.Cli.csproj b/Unity.Services.Cli/Unity.Services.Cli/Unity.Services.Cli.csproj index 34dae5d..4ebdfda 100644 --- a/Unity.Services.Cli/Unity.Services.Cli/Unity.Services.Cli.csproj +++ b/Unity.Services.Cli/Unity.Services.Cli/Unity.Services.Cli.csproj @@ -5,7 +5,7 @@ 10 ugs 1.0.0 - beta.5 + beta.6 true true true @@ -28,6 +28,7 @@ +