Skip to content
This repository was archived by the owner on Nov 1, 2023. It is now read-only.

Commit 9bb7fc9

Browse files
authored
Tests!
1 parent a57ff8d commit 9bb7fc9

File tree

3 files changed

+258
-7
lines changed

3 files changed

+258
-7
lines changed

src/ApiService/ApiService/onefuzzlib/Request.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public interface IRequestHandling {
1414
}
1515

1616
// See: https://www.rfc-editor.org/rfc/rfc7807#section-3
17-
public sealed class ProblemDetails {
17+
public sealed record ProblemDetails {
1818
[JsonConstructor]
1919
public ProblemDetails(int status, string title, string detail) {
2020
Status = status;
@@ -45,14 +45,14 @@ public ProblemDetails(HttpStatusCode code, Error error) {
4545
/// change from occurrence to occurrence of the problem, except for purposes
4646
/// of localization (e.g., using proactive content negotiation; see
4747
/// [RFC7231], Section 3.4).
48-
public string Title { get; set; }
48+
public string Title { get; }
4949

5050
/// The HTTP status code ([RFC7231], Section 6) generated by the origin
5151
/// server for this occurrence of the problem.
52-
public int Status { get; set; }
52+
public int Status { get; }
5353

5454
// A human-readable explanation specific to this occurrence of the problem.
55-
public string Detail { get; set; }
55+
public string Detail { get; }
5656
}
5757

5858
public class RequestHandling : IRequestHandling {

src/ApiService/IntegrationTests/Fakes/TestContext.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ public TestContext(ILogTracer logTracer, IStorage storage, ICreds creds, string
3636
ConfigOperations = new ConfigOperations(logTracer, this, cache);
3737
PoolOperations = new PoolOperations(logTracer, this);
3838
ScalesetOperations = new ScalesetOperations(logTracer, this);
39+
ReproOperations = new ReproOperations(logTracer, this);
40+
Reports = new Reports(logTracer, Containers);
3941
UserCredentials = new UserCredentials(logTracer, ConfigOperations);
4042
}
4143

@@ -49,6 +51,7 @@ public Async.Task InsertAll(params EntityBase[] objs)
4951
Node n => NodeOperations.Insert(n),
5052
Pool p => PoolOperations.Insert(p),
5153
Job j => JobOperations.Insert(j),
54+
Repro r => ReproOperations.Insert(r),
5255
NodeTasks nt => NodeTasksOperations.Insert(nt),
5356
InstanceConfig ic => ConfigOperations.Insert(ic),
5457
_ => throw new NotSupportedException($"You will need to add an TestContext.InsertAll case for {x.GetType()} entities"),
@@ -78,6 +81,8 @@ public Async.Task InsertAll(params EntityBase[] objs)
7881
public IPoolOperations PoolOperations { get; }
7982
public IScalesetOperations ScalesetOperations { get; }
8083
public IVmssOperations VmssOperations { get; }
84+
public IReproOperations ReproOperations { get; }
85+
public IReports Reports { get; }
8186
public EntityConverter EntityConverter { get; }
8287

8388
// -- Remainder not implemented --
@@ -100,9 +105,6 @@ public Async.Task InsertAll(params EntityBase[] objs)
100105

101106
public IProxyOperations ProxyOperations => throw new System.NotImplementedException();
102107

103-
public IReports Reports => throw new System.NotImplementedException();
104-
105-
public IReproOperations ReproOperations => throw new System.NotImplementedException();
106108

107109
public IScheduler Scheduler => throw new System.NotImplementedException();
108110

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Net;
5+
using System.Text.Json;
6+
using IntegrationTests.Fakes;
7+
using Microsoft.OneFuzz.Service;
8+
using Microsoft.OneFuzz.Service.Functions;
9+
using Microsoft.OneFuzz.Service.OneFuzzLib.Orm;
10+
using Xunit;
11+
using Xunit.Abstractions;
12+
using Async = System.Threading.Tasks;
13+
14+
namespace IntegrationTests.Functions;
15+
16+
[Trait("Category", "Live")]
17+
public class AzureStorageReproVmssTest : ReproVmssTestBase {
18+
public AzureStorageReproVmssTest(ITestOutputHelper output)
19+
: base(output, Integration.AzureStorage.FromEnvironment()) { }
20+
}
21+
22+
public class AzuriteReproVmssTest : ReproVmssTestBase {
23+
public AzuriteReproVmssTest(ITestOutputHelper output)
24+
: base(output, new Integration.AzuriteStorage()) { }
25+
}
26+
27+
public abstract class ReproVmssTestBase : FunctionTestBase {
28+
public ReproVmssTestBase(ITestOutputHelper output, IStorage storage)
29+
: base(output, storage) { }
30+
31+
32+
[Theory]
33+
[InlineData("POST", RequestType.Agent)]
34+
[InlineData("POST", RequestType.NoAuthorization)]
35+
[InlineData("GET", RequestType.Agent)]
36+
[InlineData("GET", RequestType.NoAuthorization)]
37+
[InlineData("DELETE", RequestType.Agent)]
38+
[InlineData("DELETE", RequestType.NoAuthorization)]
39+
public async Async.Task UserAuthorization_IsRequired(string method, RequestType authType) {
40+
var auth = new TestEndpointAuthorization(authType, Logger, Context);
41+
var func = new ReproVmss(Logger, auth, Context);
42+
var result = await func.Run(TestHttpRequestData.Empty(method));
43+
Assert.Equal(HttpStatusCode.Unauthorized, result.StatusCode);
44+
}
45+
46+
[Fact]
47+
public async Async.Task GetMissingVmFails() {
48+
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
49+
var func = new ReproVmss(Logger, auth, Context);
50+
var req = new ReproGet(VmId: Guid.NewGuid());
51+
var result = await func.Run(TestHttpRequestData.FromJson("GET", req));
52+
// TODO: should this be 404?
53+
Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode);
54+
var err = BodyAs<ProblemDetails>(result);
55+
Assert.Equal("no such VM", err.Detail);
56+
}
57+
58+
[Fact]
59+
public async Async.Task GetAvailableVMsCanReturnEmpty() {
60+
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
61+
var func = new ReproVmss(Logger, auth, Context);
62+
var req = new ReproGet(VmId: null); // this means "all available"
63+
var result = await func.Run(TestHttpRequestData.FromJson("GET", req));
64+
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
65+
Assert.Empty(BodyAs<Repro[]>(result));
66+
}
67+
68+
[Fact]
69+
public async Async.Task GetAvailableVMsCanReturnVM() {
70+
var vmId = Guid.NewGuid();
71+
72+
await Context.InsertAll(
73+
new Repro(
74+
VmId: vmId,
75+
TaskId: Guid.NewGuid(),
76+
new ReproConfig(Container.Parse("abcd"), "", 12345),
77+
Auth: null,
78+
Os: Os.Linux));
79+
80+
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
81+
var func = new ReproVmss(Logger, auth, Context);
82+
var req = new ReproGet(VmId: null); // this means "all available"
83+
var result = await func.Run(TestHttpRequestData.FromJson("GET", req));
84+
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
85+
var repro = Assert.Single(BodyAs<Repro[]>(result));
86+
Assert.Equal(vmId, repro.VmId);
87+
}
88+
89+
[Fact]
90+
public async Async.Task GetAvailableVMsCanReturnSpecificVM() {
91+
var vmId = Guid.NewGuid();
92+
93+
await Context.InsertAll(
94+
new Repro(
95+
VmId: vmId,
96+
TaskId: Guid.NewGuid(),
97+
new ReproConfig(Container.Parse("abcd"), "", 12345),
98+
Auth: null,
99+
Os: Os.Linux));
100+
101+
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
102+
var func = new ReproVmss(Logger, auth, Context);
103+
var req = new ReproGet(VmId: vmId);
104+
var result = await func.Run(TestHttpRequestData.FromJson("GET", req));
105+
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
106+
Assert.Equal(vmId, BodyAs<Repro>(result).VmId);
107+
}
108+
109+
110+
[Fact]
111+
public async Async.Task GetAvailableVMsDoesNotReturnUnavailableVMs() {
112+
await Context.InsertAll(
113+
new Repro(
114+
VmId: Guid.NewGuid(),
115+
TaskId: Guid.NewGuid(),
116+
new ReproConfig(Container.Parse("abcd"), "", 12345),
117+
Auth: null,
118+
Os: Os.Linux,
119+
State: VmState.Stopping),
120+
new Repro(
121+
VmId: Guid.NewGuid(),
122+
TaskId: Guid.NewGuid(),
123+
new ReproConfig(Container.Parse("abcd"), "", 12345),
124+
Auth: null,
125+
Os: Os.Linux,
126+
State: VmState.Stopped));
127+
128+
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
129+
var func = new ReproVmss(Logger, auth, Context);
130+
var req = new ReproGet(VmId: null); // this means "all available"
131+
var result = await func.Run(TestHttpRequestData.FromJson("GET", req));
132+
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
133+
Assert.Empty(BodyAs<Repro[]>(result));
134+
}
135+
136+
[Fact]
137+
public async Async.Task CannotCreateVMWithoutCredentials() {
138+
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
139+
140+
var func = new ReproVmss(Logger, auth, Context);
141+
var req = new ReproCreate(Container.Parse("abcd"), "/", 12345);
142+
var result = await func.Run(TestHttpRequestData.FromJson("POST", req));
143+
Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode);
144+
var err = BodyAs<ProblemDetails>(result);
145+
Assert.Equal(new ProblemDetails(400, "INVALID_REQUEST", "unable to find authorization token"), err);
146+
}
147+
148+
[Fact]
149+
public async Async.Task CannotCreateVMForMissingReport() {
150+
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
151+
152+
// setup fake user
153+
var userInfo = new UserInfo(Guid.NewGuid(), Guid.NewGuid(), "upn");
154+
Context.UserCredentials = new TestUserCredentials(Logger, Context.ConfigOperations, OneFuzzResult.Ok(userInfo));
155+
156+
var func = new ReproVmss(Logger, auth, Context);
157+
var req = new ReproCreate(Container.Parse("abcd"), "/", 12345);
158+
var result = await func.Run(TestHttpRequestData.FromJson("POST", req));
159+
Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode);
160+
var err = BodyAs<ProblemDetails>(result);
161+
Assert.Equal(new ProblemDetails(400, "UNABLE_TO_FIND", "unable to find report"), err);
162+
}
163+
164+
private async Async.Task<(Container, string)> CreateContainerWithReport(Guid jobId, Guid taskId) {
165+
var container = Container.Parse(Guid.NewGuid().ToString("N"));
166+
var filename = "report.json";
167+
// Setup container with Report
168+
var cc = GetContainerClient(container);
169+
_ = await cc.CreateIfNotExistsAsync();
170+
using (var ms = new MemoryStream()) {
171+
var emptyReport = new Report(
172+
null,
173+
null,
174+
"",
175+
"",
176+
"",
177+
new List<string>(),
178+
"",
179+
"",
180+
null,
181+
TaskId: taskId,
182+
JobId: jobId,
183+
null,
184+
null,
185+
null,
186+
null,
187+
null,
188+
null,
189+
null,
190+
null);
191+
192+
JsonSerializer.Serialize(ms, emptyReport, EntityConverter.GetJsonSerializerOptions());
193+
_ = ms.Seek(0, SeekOrigin.Begin);
194+
_ = await cc.UploadBlobAsync(filename, ms);
195+
}
196+
197+
return (container, filename);
198+
}
199+
200+
[Fact]
201+
public async Async.Task CannotCreateVMForMissingTask() {
202+
var (container, filename) = await CreateContainerWithReport(Guid.NewGuid(), Guid.NewGuid());
203+
204+
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
205+
206+
// setup fake user
207+
var userInfo = new UserInfo(Guid.NewGuid(), Guid.NewGuid(), "upn");
208+
Context.UserCredentials = new TestUserCredentials(Logger, Context.ConfigOperations, OneFuzzResult.Ok(userInfo));
209+
210+
var func = new ReproVmss(Logger, auth, Context);
211+
var req = new ReproCreate(container, filename, 12345);
212+
var result = await func.Run(TestHttpRequestData.FromJson("POST", req));
213+
Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode);
214+
var err = BodyAs<ProblemDetails>(result);
215+
Assert.Equal(new ProblemDetails(400, "INVALID_REQUEST", "unable to find task"), err);
216+
}
217+
218+
[Fact]
219+
public async Async.Task CanCreateVMSuccessfully() {
220+
// report must have TaskID pointing to a valid Task
221+
222+
var jobId = Guid.NewGuid();
223+
var taskId = Guid.NewGuid();
224+
var (container, filename) = await CreateContainerWithReport(jobId: jobId, taskId: taskId);
225+
await Context.InsertAll(
226+
new Task(
227+
JobId: jobId,
228+
TaskId: taskId,
229+
TaskState.Running,
230+
Os.Linux,
231+
new TaskConfig(
232+
JobId: jobId,
233+
null,
234+
new TaskDetails(TaskType.LibfuzzerFuzz, 12345))));
235+
236+
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
237+
238+
// setup fake user
239+
var userInfo = new UserInfo(Guid.NewGuid(), Guid.NewGuid(), "upn");
240+
Context.UserCredentials = new TestUserCredentials(Logger, Context.ConfigOperations, OneFuzzResult.Ok(userInfo));
241+
242+
var func = new ReproVmss(Logger, auth, Context);
243+
var req = new ReproCreate(container, filename, 12345);
244+
var result = await func.Run(TestHttpRequestData.FromJson("POST", req));
245+
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
246+
var repro = BodyAs<Repro>(result);
247+
Assert.Equal(taskId, repro.TaskId);
248+
}
249+
}

0 commit comments

Comments
 (0)