Files
onefuzz/src/ApiService/IntegrationTests/ScalesetTests.cs
George Pollard 38dfa668bc Standardize HTTP error results, better rejection message when parsing validated strings (#2663)
1. When parsing a `ValidatedString` from JSON and it fails, include a message about the expected format of the string.
   - Reworked the classes using C#11 features to reduce the amount of boilerplate needed to add a new validated string type.

2. Change to use [RFC7807](https://www.rfc-editor.org/rfc/rfc7807) format for HTTP error responses. At the moment we returned the `Error` type which was undocumented.
3. Update CLI to parse RFC7807 responses.

Old error looked like:

```console
$ onefuzz containers create AbCd
ERROR:cli:command failed: request did not succeed: HTTP 500 -
```

New error looks like:

```console
$ onefuzz containers create AbCd
ERROR:cli:command failed: request did not succeed (400: INVALID_REQUEST): Unable
to parse 'AbCd' as a Container: Container name must be 3-63 lowercase letters, numbers,
or non-consecutive hyphens (see: https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/resource-name-rules#microsoftstorage)
```

Closes #2661.
2022-12-02 10:33:14 +13:00

106 lines
4.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Net;
using IntegrationTests.Fakes;
using Microsoft.OneFuzz.Service;
using Xunit;
using Xunit.Abstractions;
using Async = System.Threading.Tasks;
using ScalesetFunction = Microsoft.OneFuzz.Service.Functions.Scaleset;
namespace IntegrationTests.Functions;
[Trait("Category", "Live")]
public class AzureStorageScalesetTest : ScalesetTestBase {
public AzureStorageScalesetTest(ITestOutputHelper output)
: base(output, Integration.AzureStorage.FromEnvironment()) { }
}
public class AzuriteScalesetTest : ScalesetTestBase {
public AzuriteScalesetTest(ITestOutputHelper output)
: base(output, new Integration.AzuriteStorage()) { }
}
public abstract class ScalesetTestBase : FunctionTestBase {
public ScalesetTestBase(ITestOutputHelper output, IStorage storage)
: base(output, storage) { }
[Theory]
[InlineData("POST", RequestType.Agent)]
[InlineData("POST", RequestType.NoAuthorization)]
[InlineData("PATCH", RequestType.Agent)]
[InlineData("PATCH", RequestType.NoAuthorization)]
[InlineData("GET", RequestType.Agent)]
[InlineData("GET", RequestType.NoAuthorization)]
[InlineData("DELETE", RequestType.Agent)]
[InlineData("DELETE", RequestType.NoAuthorization)]
public async Async.Task UserAuthorization_IsRequired(string method, RequestType authType) {
var auth = new TestEndpointAuthorization(authType, Logger, Context);
var func = new ScalesetFunction(Logger, auth, Context);
var result = await func.Run(TestHttpRequestData.Empty(method));
Assert.Equal(HttpStatusCode.Unauthorized, result.StatusCode);
}
[Fact]
public async Async.Task Search_SpecificScaleset_ReturnsErrorIfNoneFound() {
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
var req = new ScalesetSearch(ScalesetId: Guid.NewGuid());
var func = new ScalesetFunction(Logger, auth, Context);
var result = await func.Run(TestHttpRequestData.FromJson("GET", req));
Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode);
var err = BodyAs<ProblemDetails>(result);
Assert.Equal("unable to find scaleset", err.Detail);
}
[Fact]
public async Async.Task Search_AllScalesets_ReturnsEmptyIfNoneFound() {
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
var req = new ScalesetSearch();
var func = new ScalesetFunction(Logger, auth, Context);
var result = await func.Run(TestHttpRequestData.FromJson("GET", req));
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
Assert.Equal("[]", BodyAsString(result));
}
[Fact]
public async Async.Task Create_Scaleset() {
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
// override the found user credentials
var userObjectId = Guid.NewGuid();
var userInfo = new UserInfo(ApplicationId: Guid.NewGuid(), ObjectId: userObjectId, "upn");
Context.UserCredentials = new TestUserCredentials(Logger, Context.ConfigOperations, OneFuzzResult<UserInfo>.Ok(userInfo));
var poolName = PoolName.Parse("mypool");
await Context.InsertAll(
// user must be admin
new InstanceConfig(Context.ServiceConfiguration.OneFuzzInstanceName!) { Admins = new[] { userObjectId } },
// pool must exist and be managed
new Pool(poolName, Guid.NewGuid(), Os.Linux, Managed: true, Architecture.x86_64, PoolState.Running));
var req = new ScalesetCreate(
poolName,
TestVmssOperations.TestSku,
"Image",
Region: null,
Size: 1,
SpotInstances: false,
Tags: new Dictionary<string, string>());
var func = new ScalesetFunction(Logger, auth, Context);
var result = await func.Run(TestHttpRequestData.FromJson("POST", req));
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
var resp = BodyAs<ScalesetResponse>(result);
Assert.Equal(poolName, resp.PoolName);
// auth should not be included in the response
Assert.Null(resp.Auth);
}
}