diff --git a/.gitignore b/.gitignore index a37957a..8dd0034 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,9 @@ bin/ obj/ .vscode/ .vs/ +*.user + +packages/ + +*.ncrunchsolution +_NCrunch_*/ diff --git a/examples/Makefile b/Makefile similarity index 100% rename from examples/Makefile rename to Makefile diff --git a/duo_api_csharp.sln b/duo_api_csharp.sln index f52460a..743ca5f 100644 --- a/duo_api_csharp.sln +++ b/duo_api_csharp.sln @@ -1,9 +1,18 @@ - Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2024 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32126.317 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "duo_api_csharp", "duo_api_csharp.csproj", "{6E96C9D9-0825-4D26-83C7-8A62180F8FB9}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "duo_api_csharp", "duo_api_csharp\duo_api_csharp.csproj", "{6E96C9D9-0825-4D26-83C7-8A62180F8FB9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DuoApiTest", "test\DuoApiTest.csproj", "{6B97B9FB-E553-494C-BD50-4BF7DB5C2184}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E739E3FE-D923-480A-9B01-3B2A623067E3}" + ProjectSection(SolutionItems) = preProject + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Examples", "Examples\Examples.csproj", "{C089A10B-646D-407E-A2B8-848C6C522B13}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,6 +24,14 @@ Global {6E96C9D9-0825-4D26-83C7-8A62180F8FB9}.Debug|Any CPU.Build.0 = Debug|Any CPU {6E96C9D9-0825-4D26-83C7-8A62180F8FB9}.Release|Any CPU.ActiveCfg = Release|Any CPU {6E96C9D9-0825-4D26-83C7-8A62180F8FB9}.Release|Any CPU.Build.0 = Release|Any CPU + {6B97B9FB-E553-494C-BD50-4BF7DB5C2184}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6B97B9FB-E553-494C-BD50-4BF7DB5C2184}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6B97B9FB-E553-494C-BD50-4BF7DB5C2184}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6B97B9FB-E553-494C-BD50-4BF7DB5C2184}.Release|Any CPU.Build.0 = Release|Any CPU + {C089A10B-646D-407E-A2B8-848C6C522B13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C089A10B-646D-407E-A2B8-848C6C522B13}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C089A10B-646D-407E-A2B8-848C6C522B13}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C089A10B-646D-407E-A2B8-848C6C522B13}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Duo.cs b/duo_api_csharp/Duo.cs similarity index 100% rename from Duo.cs rename to duo_api_csharp/Duo.cs diff --git a/duo_api_csharp.csproj b/duo_api_csharp/duo_api_csharp.csproj similarity index 51% rename from duo_api_csharp.csproj rename to duo_api_csharp/duo_api_csharp.csproj index 8eb35f2..88a1660 100644 --- a/duo_api_csharp.csproj +++ b/duo_api_csharp/duo_api_csharp.csproj @@ -1,89 +1,54 @@ - - - Debug - AnyCPU - {6E96C9D9-0825-4D26-83C7-8A62180F8FB9} - Library - false - ClassLibrary - v4.5 - 512 - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - false - - - duo_api_csharp - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + Debug + AnyCPU + {6E96C9D9-0825-4D26-83C7-8A62180F8FB9} + Library + false + ClassLibrary + v4.8 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + duo_api_csharp + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/App.config b/examples/App.config new file mode 100644 index 0000000..193aecc --- /dev/null +++ b/examples/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/Examples.csproj b/examples/Examples.csproj new file mode 100644 index 0000000..35bc283 --- /dev/null +++ b/examples/Examples.csproj @@ -0,0 +1,59 @@ + + + + + Debug + AnyCPU + {C089A10B-646D-407E-A2B8-848C6C522B13} + Exe + Examples + Examples + v4.8 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + {6e96c9d9-0825-4d26-83c7-8a62180f8fb9} + duo_api_csharp + + + + \ No newline at end of file diff --git a/examples/DuoAdmin.cs b/examples/Program.cs similarity index 99% rename from examples/DuoAdmin.cs rename to examples/Program.cs index 79c46a3..0125670 100644 --- a/examples/DuoAdmin.cs +++ b/examples/Program.cs @@ -6,7 +6,7 @@ using System; using System.Collections.Generic; -class DuoAdmin +public class Program { static int Main(string[] args) { diff --git a/examples/Properties/AssemblyInfo.cs b/examples/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6a556c3 --- /dev/null +++ b/examples/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Examples")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Examples")] +[assembly: AssemblyCopyright("Copyright © 2022")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c089a10b-646d-407e-a2b8-848c6c522b13")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/test/.gitignore b/test/.gitignore deleted file mode 100644 index 23131a8..0000000 --- a/test/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -TestResult.xml -DuoApiTest.suo -DuoApiTest.csproj.* -bin -obj diff --git a/test/ApiCallTest.cs b/test/ApiCallTest.cs index 45ef524..23d67f6 100644 --- a/test/ApiCallTest.cs +++ b/test/ApiCallTest.cs @@ -4,8 +4,7 @@ using System.Threading; using System.Collections.Generic; using System.IO; - -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Xunit; // Subclass DuoApi so we can test using HTTP rather than HTTPS public class TestDuoApi : DuoApi @@ -61,7 +60,8 @@ public TestServer(string ikey, string skey) } public delegate string TestDispatchHandler(HttpListenerContext ctx); - public TestDispatchHandler handler { + public TestDispatchHandler handler + { get { @@ -70,7 +70,8 @@ public TestDispatchHandler handler { return this._handler; } } - set { + set + { lock (this) { this._handler = value; @@ -124,17 +125,20 @@ public void Run() private string skey; private TestDispatchHandler _handler; } - - -[TestClass] public class TestApiCall { private const string test_ikey = "DI9FD6NAKXN4B9DTCCB7"; private const string test_skey = "RScfSuMrpL52TaciEhGtZkGjg8W4JSe5luPL63J8"; private const string test_host = "localhost:8080"; - [TestInitialize] - public void SetUp() + private TestServer srv; + private Thread srvThread; + private TestDuoApi api; + + /// + /// + /// + public TestApiCall() { api = new TestDuoApi(test_ikey, test_skey, test_host); srv = new TestServer(test_ikey, test_skey); @@ -142,44 +146,44 @@ public void SetUp() srvThread.Start(); } - [TestCleanup] - public void CleanUp() + ~TestApiCall() { srvThread.Join(); } - [TestMethod] + [Fact] public void Test401Response() { - srv.handler = delegate(HttpListenerContext ctx) { + srv.handler = delegate (HttpListenerContext ctx) + { ctx.Response.StatusCode = 401; return "Hello, Unauthorized World!"; }; HttpStatusCode code; string response = api.ApiCall("GET", "/401", new Dictionary(), 10000, out code); - Assert.AreEqual(code, HttpStatusCode.Unauthorized); - Assert.AreEqual(response, "Hello, Unauthorized World!"); + Assert.Equal(HttpStatusCode.Unauthorized, code); + Assert.Equal("Hello, Unauthorized World!", response); } - [TestMethod] + [Fact] public void Test200Response() { - srv.handler = delegate(HttpListenerContext ctx) + srv.handler = delegate (HttpListenerContext ctx) { return "Hello, World!"; }; HttpStatusCode code; string response = api.ApiCall("GET", "/hello", new Dictionary(), 10000, out code); - Assert.AreEqual(code, HttpStatusCode.OK); - Assert.AreEqual(response, "Hello, World!"); + Assert.Equal(HttpStatusCode.OK, code); + Assert.Equal("Hello, World!", response); } - [TestMethod] + [Fact] public void TestDefaultUserAgent() { - srv.handler = delegate(HttpListenerContext ctx) + srv.handler = delegate (HttpListenerContext ctx) { Console.WriteLine(String.Format("User-Agent is: {0}", ctx.Request.UserAgent)); return ctx.Request.UserAgent; @@ -187,26 +191,26 @@ public void TestDefaultUserAgent() HttpStatusCode code; string response = api.ApiCall("GET", "/DefaultUserAgent", new Dictionary(), 10000, out code); - Assert.AreEqual(code, HttpStatusCode.OK); - StringAssert.StartsWith(response, api.DEFAULT_AGENT); + Assert.Equal(HttpStatusCode.OK, code); + Assert.StartsWith(api.DEFAULT_AGENT, response); } - [TestMethod] + [Fact] public void TestCustomUserAgent() { api = new TestDuoApi(test_ikey, test_skey, test_host, "CustomUserAgent/1.0"); - srv.handler = delegate(HttpListenerContext ctx) + srv.handler = delegate (HttpListenerContext ctx) { return ctx.Request.UserAgent; }; HttpStatusCode code; string response = api.ApiCall("GET", "/CustomUserAgent", new Dictionary(), 10000, out code); - Assert.AreEqual(code, HttpStatusCode.OK); - Assert.AreEqual("CustomUserAgent/1.0", response); + Assert.Equal(HttpStatusCode.OK, code); + Assert.Equal("CustomUserAgent/1.0", response); } - [TestMethod] + [Fact] public void TestGetParameterSigning() { Dictionary parameters = new Dictionary @@ -215,7 +219,8 @@ public void TestGetParameterSigning() {"param2", "bar"} }; - srv.handler = delegate(HttpListenerContext ctx) { + srv.handler = delegate (HttpListenerContext ctx) + { if (ctx.Request.HttpMethod != "GET") { return "bad method!"; @@ -241,15 +246,15 @@ public void TestGetParameterSigning() return "bad signature"; } return "OK"; - + }; HttpStatusCode code; string response = api.ApiCall("GET", "/get_params", parameters, 10000, out code); - Assert.AreEqual("OK", response); + Assert.Equal("OK", response); } - [TestMethod] + [Fact] public void TestPostParameterSigning() { Dictionary parameters = new Dictionary @@ -258,7 +263,7 @@ public void TestPostParameterSigning() {"param2", "bar"} }; - srv.handler = delegate(HttpListenerContext ctx) + srv.handler = delegate (HttpListenerContext ctx) { if (ctx.Request.HttpMethod != "POST") { @@ -290,10 +295,10 @@ public void TestPostParameterSigning() HttpStatusCode code; string response = api.ApiCall("POST", "/get_params", parameters, 10000, out code); - Assert.AreEqual("OK", response); + Assert.Equal("OK", response); } - [TestMethod] + [Fact] public void TestPostParameterSigningCustomDate() { Dictionary parameters = new Dictionary @@ -302,7 +307,7 @@ public void TestPostParameterSigningCustomDate() {"param2", "bar"} }; - srv.handler = delegate(HttpListenerContext ctx) + srv.handler = delegate (HttpListenerContext ctx) { if (ctx.Request.HttpMethod != "POST") { @@ -340,46 +345,40 @@ public void TestPostParameterSigningCustomDate() DateTime date = new DateTime(2013, 11, 11, 22, 34, 00, DateTimeKind.Utc); HttpStatusCode status_code; string response = api.ApiCall("POST", "/get_params", parameters, 10000, date, out status_code); - Assert.AreEqual("OK", response); + Assert.Equal("OK", response); } - [TestMethod] + [Fact] public void TestJsonTimeout() { - srv.handler = delegate(HttpListenerContext ctx) + srv.handler = delegate (HttpListenerContext ctx) { Thread.Sleep(2 * 1000); return "You should've timed out!"; }; - try + var ex = Record.Exception(() => { string response = api.JSONApiCall("GET", "/timeout", new Dictionary(), 500); - Assert.Fail("Did not raise a Timeout exception!"); - } - catch (WebException ex) - { - Assert.AreEqual(ex.Status, WebExceptionStatus.Timeout); - } - catch (Exception ex) - { - Assert.Fail("Raised the wrong type of Exception: {0}", ex); - } + }); + + var we = Assert.IsType(ex); + Assert.Equal(WebExceptionStatus.Timeout, we.Status); } - [TestMethod] + [Fact] public void TestValidJsonResponse() { - srv.handler = delegate(HttpListenerContext ctx) + srv.handler = delegate (HttpListenerContext ctx) { return "{\"stat\": \"OK\", \"response\": \"hello, world!\"}"; }; - string response = api.JSONApiCall("GET", "/json_ok", new Dictionary()); - Assert.AreEqual(response, "hello, world!"); + string response = api.JSONApiCall("GET", "/json_ok", new Dictionary()); + Assert.Equal("hello, world!", response); } - [TestMethod] + [Fact] public void TestValidJsonPagingResponseNoParameters() { srv.handler = delegate (HttpListenerContext ctx) @@ -388,14 +387,14 @@ public void TestValidJsonPagingResponseNoParameters() }; var parameters = new Dictionary(); var jsonResponse = api.JSONPagingApiCall("GET", "/json_ok", parameters, 0, 10); - Assert.AreEqual(jsonResponse["response"], "hello, world!"); + Assert.Equal("hello, world!", jsonResponse["response"]); var metadata = jsonResponse["metadata"] as Dictionary; - Assert.AreEqual(metadata["next_offset"], 10); + Assert.Equal(10, metadata["next_offset"]); // make sure parameters was not changed as a side-effect - Assert.AreEqual(parameters.Count, 0); + Assert.Empty(parameters); } - [TestMethod] + [Fact] public void TestValidJsonPagingResponseExistingParameters() { srv.handler = delegate (HttpListenerContext ctx) @@ -408,80 +407,86 @@ public void TestValidJsonPagingResponseExistingParameters() {"limit", "10"} }; var jsonResponse = api.JSONPagingApiCall("GET", "/json_ok", parameters, 10, 20); - Assert.AreEqual(jsonResponse["response"], "hello, world!"); + Assert.Equal("hello, world!", jsonResponse["response"]); var metadata = jsonResponse["metadata"] as Dictionary; - Assert.IsFalse(metadata.ContainsKey("next_offset")); + Assert.False(metadata.ContainsKey("next_offset")); // make sure parameters was not changed as a side-effect - Assert.AreEqual(parameters.Count, 2); - Assert.AreEqual(parameters["offset"], "0"); - Assert.AreEqual(parameters["limit"], "10"); + Assert.Equal(2, parameters.Count); + Assert.Equal("0", parameters["offset"]); + Assert.Equal("10", parameters["limit"]); } - [TestMethod] + [Fact] public void TestErrorJsonResponse() { - srv.handler = delegate(HttpListenerContext ctx) + srv.handler = delegate (HttpListenerContext ctx) { ctx.Response.StatusCode = 400; return "{\"stat\": \"FAIL\", \"message\": \"Missing required request parameters\", \"code\": 40001, \"message_detail\": \"user_id or username\"}"; }; - try { - string response = api.JSONApiCall("GET", "/json_error", new Dictionary()); - Assert.Fail("Didn't raise ApiException"); - } - catch (ApiException e) + + var ex = Record.Exception(() => { - Assert.AreEqual(e.HttpStatus, 400); - Assert.AreEqual(e.Code, 40001); - Assert.AreEqual(e.ApiMessage, "Missing required request parameters"); - Assert.AreEqual(e.ApiMessageDetail, "user_id or username"); - } + string response = api.JSONApiCall("GET", "/json_error", new Dictionary()); + }); + + Assert.NotNull(ex); + var e = Assert.IsType(ex); + + + Assert.Equal(400, e.HttpStatus); + Assert.Equal(40001, e.Code); + Assert.Equal("Missing required request parameters", e.ApiMessage); + Assert.Equal("user_id or username", e.ApiMessageDetail); + } - [TestMethod] + [Fact] public void TestJsonResponseMissingField() { - srv.handler = delegate(HttpListenerContext ctx) + srv.handler = delegate (HttpListenerContext ctx) { ctx.Response.StatusCode = 400; return "{\"message\": \"Missing required request parameters\", \"code\": 40001, \"message_detail\": \"user_id or username\"}"; }; - try + + var ex = Record.Exception(() => { string response = api.JSONApiCall("GET", "/json_missing_field", new Dictionary()); - Assert.Fail("Didn't raise ApiException"); - } - catch (BadResponseException e) - { - Assert.AreEqual(e.HttpStatus, 400); - } + }); + + Assert.NotNull(ex); + var e = Assert.IsType(ex); + + Assert.Equal(400, e.HttpStatus); + } - [TestMethod] + [Fact] public void TestJsonUnparseableResponse() { - srv.handler = delegate(HttpListenerContext ctx) + srv.handler = delegate (HttpListenerContext ctx) { ctx.Response.StatusCode = 500; return "this is not json"; }; - try + var ex = Record.Exception(() => { string response = api.JSONApiCall("GET", "/json_bad", new Dictionary()); - Assert.Fail("Didn't raise ApiException"); - } - catch (BadResponseException e) - { - Assert.AreEqual(e.HttpStatus, 500); - } + }); + + Assert.NotNull(ex); + var e = Assert.IsType(ex); + Assert.Equal(500, e.HttpStatus); + } - [TestMethod] + [Fact] public void TestRateLimitThenSuccess() { List statusCodes = new List() { 429, 200 }; int callCount = 0; - srv.handler = delegate(HttpListenerContext ctx) + srv.handler = delegate (HttpListenerContext ctx) { callCount++; ctx.Response.StatusCode = statusCodes[0]; @@ -493,19 +498,19 @@ public void TestRateLimitThenSuccess() HttpStatusCode code; string response = api.ApiCall("GET", "/hello", new Dictionary(), 10000, out code); - Assert.AreEqual(code, HttpStatusCode.OK); - Assert.AreEqual(api.sleeper.sleepCalls.Count, 1); - Assert.AreEqual(api.sleeper.sleepCalls[0], 1123); - Assert.AreEqual(api.random.randomCalls.Count, 1); - Assert.AreEqual(api.random.randomCalls[0], 1001); + Assert.Equal(HttpStatusCode.OK, code); + Assert.Single(api.sleeper.sleepCalls); + Assert.Equal(1123, api.sleeper.sleepCalls[0]); + Assert.Single(api.random.randomCalls); + Assert.Equal(1001, api.random.randomCalls[0]); } - [TestMethod] + [Fact] public void TestRateLimitedCompletely() { List statusCodes = new List() { 429, 429, 429, 429, 429, 429, 429 }; int callCount = 0; - srv.handler = delegate(HttpListenerContext ctx) + srv.handler = delegate (HttpListenerContext ctx) { callCount++; ctx.Response.StatusCode = statusCodes[0]; @@ -517,26 +522,22 @@ public void TestRateLimitedCompletely() HttpStatusCode code; string response = api.ApiCall("GET", "/hello", new Dictionary(), 10000, out code); - Assert.AreEqual(code, (HttpStatusCode) 429); - Assert.AreEqual(callCount, 7); - Assert.AreEqual(api.sleeper.sleepCalls.Count, 6); - Assert.AreEqual(api.sleeper.sleepCalls[0], 1123); - Assert.AreEqual(api.sleeper.sleepCalls[1], 2123); - Assert.AreEqual(api.sleeper.sleepCalls[2], 4123); - Assert.AreEqual(api.sleeper.sleepCalls[3], 8123); - Assert.AreEqual(api.sleeper.sleepCalls[4], 16123); - Assert.AreEqual(api.sleeper.sleepCalls[5], 32123); - - Assert.AreEqual(api.random.randomCalls.Count, 6); - Assert.AreEqual(api.random.randomCalls[0], 1001); - Assert.AreEqual(api.random.randomCalls[1], 1001); - Assert.AreEqual(api.random.randomCalls[2], 1001); - Assert.AreEqual(api.random.randomCalls[3], 1001); - Assert.AreEqual(api.random.randomCalls[4], 1001); - Assert.AreEqual(api.random.randomCalls[5], 1001); + Assert.Equal(code, (HttpStatusCode)429); + Assert.Equal(7, callCount); + Assert.Equal(6, api.sleeper.sleepCalls.Count); + Assert.Equal(1123, api.sleeper.sleepCalls[0]); + Assert.Equal(2123, api.sleeper.sleepCalls[1]); + Assert.Equal(4123, api.sleeper.sleepCalls[2]); + Assert.Equal(8123, api.sleeper.sleepCalls[3]); + Assert.Equal(16123, api.sleeper.sleepCalls[4]); + Assert.Equal(32123, api.sleeper.sleepCalls[5]); + + Assert.Equal(6, api.random.randomCalls.Count); + Assert.Equal(1001, api.random.randomCalls[0]); + Assert.Equal(1001, api.random.randomCalls[1]); + Assert.Equal(1001, api.random.randomCalls[2]); + Assert.Equal(1001, api.random.randomCalls[3]); + Assert.Equal(1001, api.random.randomCalls[4]); + Assert.Equal(1001, api.random.randomCalls[5]); } - - private TestServer srv; - private Thread srvThread; - private TestDuoApi api; } \ No newline at end of file diff --git a/test/DuoApiTest.csproj b/test/DuoApiTest.csproj index 7c3ae0d..96608cc 100644 --- a/test/DuoApiTest.csproj +++ b/test/DuoApiTest.csproj @@ -1,5 +1,7 @@  - + + + Debug @@ -9,8 +11,11 @@ Properties test test - v3.5 + v4.8 512 + + + true @@ -20,6 +25,7 @@ DEBUG;TRACE prompt 4 + false pdbonly @@ -28,25 +34,86 @@ TRACE prompt 4 + false - + + ..\packages\Microsoft.Bcl.AsyncInterfaces.6.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + ..\packages\System.Text.Encodings.Web.6.0.0\lib\net461\System.Text.Encodings.Web.dll + + + ..\packages\System.Text.Json.6.0.2\lib\net461\System.Text.Json.dll + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + + + ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll + + + ..\packages\xunit.abstractions.2.0.3\lib\net35\xunit.abstractions.dll + + + ..\packages\xunit.assert.2.4.1\lib\netstandard1.1\xunit.assert.dll + + + ..\packages\xunit.extensibility.core.2.4.1\lib\net452\xunit.core.dll + + + ..\packages\xunit.extensibility.execution.2.4.1\lib\net452\xunit.execution.desktop.dll + - - Duo.cs - + + + + + + + + + + {6e96c9d9-0825-4d26-83c7-8a62180f8fb9} + duo_api_csharp + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + +