Permalink
Browse files

initial commit

  • Loading branch information...
0 parents commit a2d276933b725130f5ab7a86587d7ce54de80869 Karl Seguin committed Jan 3, 2012
@@ -0,0 +1,21 @@
+TestResult.xml
+Tests.VisualState.xml
+[Bb]in
+[Oo]bj
+[Rr]elease
+[Dd]ebug
+*.bak
+*.manifest
+*.exe
+*.dll
+*.pdb
+*.cache
+*.suo
+*.orig
+*.user
+.svn
+_ReSharper*
+.DS_Store
+*.usertasks
+*.userprefs
+Parse.Playground
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using NUnit.Framework;
+
+namespace Parse.Tests
+{
+ public abstract class BaseFixture
+ {
+ protected static readonly Func<IDictionary<string, object>> EmptyPayload = () => new Dictionary<string, object>();
+ protected virtual bool NeedAServer
+ {
+ get { return true; }
+ }
+ protected FakeServer Server;
+ protected AutoResetEvent Trigger;
+
+ [SetUp]
+ public void SetUp()
+ {
+ Trigger = new AutoResetEvent(false);
+ if (NeedAServer)
+ {
+ Server = new FakeServer();
+ ParseConfiguration.Configure("the-app-id", "shhh", c => c.ConnectTo("http://localhost:" + FakeServer.Port + "/"));
+ }
+ BeforeEachTest();
+ }
+ [TearDown]
+ public void TearDown()
+ {
+ if (Server != null)
+ {
+ Server.Dispose();
+ }
+ AfterEachTest();
+ }
+ public virtual void AfterEachTest() { }
+ public virtual void BeforeEachTest() { }
+
+ protected void AssertMogadeException(string expectedMessage, Action code)
+ {
+ var ex = Assert.Throws<ParseException>(() => code());
+ Assert.AreEqual(expectedMessage, ex.Message);
+ }
+
+ protected void SetIfSuccess(Response response)
+ {
+ if (response.Success) { Set(); }
+
+ Assert.Fail(response.Error.Message);
+ }
+ protected void Set()
+ {
+ Trigger.Set();
+ }
+ protected void WaitOne()
+ {
+ Assert.IsTrue(Trigger.WaitOne(3000), "Test terminated without properly signalling the trigger");
+ }
+ }
+
+ internal static class AutoResetEventExtensions
+ {
+ public static void Wait(this AutoResetEvent trigger, int count)
+ {
+ for (var i = 0; i < count; ++i)
+ {
+ trigger.WaitOne();
+ }
+ }
+ }
+}
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Text;
+using System.Threading;
+
+namespace Parse.Tests
+{
+ /// <summary>
+ /// A fake server for testing
+ /// </summary>
+ /// <remarks>
+ /// Kinda hate this...bound to setup a real/fake testing server somewhere at some point
+ /// </remarks>
+ public class FakeServer : IDisposable
+ {
+ private bool _disposed;
+ private readonly HttpListener _listener;
+ private readonly Thread _thread;
+ public const int Port = 9948;
+ private readonly IList<ApiExpectation> _expectations;
+ private Action _onInvoke;
+
+ public FakeServer()
+ {
+ _expectations = new List<ApiExpectation>(5);
+ _listener = new HttpListener();
+ _listener.Prefixes.Add("http://*:" + 9948 + "/");
+ _listener.Start();
+ _thread = new Thread(Listen) {IsBackground = true};
+ _thread.Start();
+ }
+
+ public void Stub(ApiExpectation expectation)
+ {
+ _expectations.Add(expectation);
+ }
+
+ private void Listen()
+ {
+ while (true)
+ {
+ var context = GetContext();
+ if (context == null) { return; }
+ var body = ExtractBody(context.Request);
+ var expectation = FindExpectation(context, body);
+ if (expectation == null)
+ {
+ SendResponse(context, string.Format("Unexpected call: {0} {1}{2}{3}", context.Request.HttpMethod, context.Request.Url, Environment.NewLine, body), new ApiExpectation { Status = 500 });
+ return;
+ }
+ if (_onInvoke != null)
+ {
+ _onInvoke();
+ }
+ SendResponse(context, body, expectation);
+ }
+ }
+
+ private static void SendResponse(HttpListenerContext context, string body, ApiExpectation expectation)
+ {
+ var response = context.Response;
+ response.StatusCode = expectation.Status ?? 200;
+ response.ContentLength64 = (expectation.Response ?? body).Length;
+ using (var sw = new StreamWriter(response.OutputStream))
+ {
+ sw.Write(expectation.Response ?? body);
+ }
+ response.Close();
+ }
+
+ private HttpListenerContext GetContext()
+ {
+ try { return _listener.GetContext(); }
+ catch (HttpListenerException) { return null; }
+ }
+
+ private ApiExpectation FindExpectation(HttpListenerContext context, string body)
+ {
+ var request = context.Request;
+ foreach (var expectation in _expectations)
+ {
+ if (expectation.Method != null && string.Compare(request.HttpMethod, expectation.Method, true) != 0) { continue; }
+ if (expectation.Url != null && string.Compare(request.Url.AbsolutePath, expectation.Url, true) != 0) { continue; }
+ if (expectation.Request != null && string.Compare(body, expectation.Request, true) != 0) { continue; }
+ return expectation; //we found a match!
+ }
+ return null;
+ }
+ private static string ExtractBody(HttpListenerRequest request)
+ {
+ if (request.HttpMethod == "GET")
+ {
+ return request.Url.Query.Length > 0 ? request.Url.Query.Substring(1) : null;
+ }
+ var buffer = new byte[request.ContentLength64];
+ request.InputStream.Read(buffer, 0, buffer.Length);
+ return Encoding.Default.GetString(buffer);
+ }
+
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+ private void Dispose(bool disposing)
+ {
+ if (!_disposed && disposing)
+ {
+ _listener.Stop();
+ }
+ _disposed = true;
+ }
+
+ public void OnInvoke(Action callback)
+ {
+ _onInvoke = callback;
+ }
+ }
+
+ /// <remarks>
+ /// All of these default to safe values, the most interesting of which is a null response will act as an echo server (return the request)
+ /// </remarks>
+ public class ApiExpectation
+ {
+ public readonly static ApiExpectation EchoAll = new ApiExpectation();
+ public string Method { get; set; }
+ public string Url { get; set; }
+ public string Request { get; set; }
+ public int? Status { get; set; }
+ public string Response { get; set; }
+ }
+}
@@ -0,0 +1,33 @@
+using System;
+using NUnit.Framework;
+
+namespace Parse.Tests.ObjectsTests
+{
+ [TestFixture]
+ public class DeleteTests : BaseFixture
+ {
+ [Test]
+ public void SendsADeleteRequest()
+ {
+ Server.Stub(new ApiExpectation { Method = "DELETE", Url = "/1/classes/ParseObjectClass/Ed1nuqPvcm", Response = "{name: 'Goku', createdAt: '2011-08-20T02:06:57.931Z', updatedAt: '2012-09-21T03:07:58.932Z', objectId: 'Ed1nuqPvcm' }" });
+ new Driver().Objects.Delete<ParseObjectClass>("Ed1nuqPvcm", r =>
+ {
+ Assert.AreEqual("Ed1nuqPvcm", r.Data.Id);
+ Assert.AreEqual(new DateTime(2011, 8, 20, 2, 6, 57, 931), r.Data.CreatedAt.Value.ToUniversalTime());
+ Assert.AreEqual(new DateTime(2012, 9, 21, 3, 7, 58, 932), r.Data.UpdatedAt.Value.ToUniversalTime());
+ Assert.AreEqual("Goku", r.Data.Name);
+ SetIfSuccess(r);
+ });
+ WaitOne();
+ }
+
+ [Test]
+ public void SendsADeleteForAParseObject()
+ {
+ var o = new ParseObjectClass {Id = "abc123"};
+ Server.Stub(new ApiExpectation { Method = "DELETE", Url = "/1/classes/ParseObjectClass/abc123", Response = "{}" });
+ new Driver().Objects.Delete(o, SetIfSuccess);
+ WaitOne();
+ }
+ }
+}
@@ -0,0 +1,24 @@
+using System;
+using NUnit.Framework;
+
+namespace Parse.Tests.ObjectsTests
+{
+ [TestFixture]
+ public class GetTests : BaseFixture
+ {
+ [Test]
+ public void SendsAGetRequest()
+ {
+ Server.Stub(new ApiExpectation { Method = "GET", Url = "/1/classes/ParseObjectClass/Ed1nuqPvcm", Response = "{name: 'Goku', createdAt: '2011-08-20T02:06:57.931Z', updatedAt: '2012-09-21T03:07:58.932Z', objectId: 'Ed1nuqPvcm' }" });
+ new Driver().Objects.Get<ParseObjectClass>("Ed1nuqPvcm", r =>
+ {
+ Assert.AreEqual("Ed1nuqPvcm", r.Data.Id);
+ Assert.AreEqual(new DateTime(2011, 8, 20, 2, 6, 57, 931), r.Data.CreatedAt.Value.ToUniversalTime());
+ Assert.AreEqual(new DateTime(2012, 9, 21, 3, 7, 58, 932), r.Data.UpdatedAt.Value.ToUniversalTime());
+ Assert.AreEqual("Goku", r.Data.Name);
+ SetIfSuccess(r);
+ });
+ WaitOne();
+ }
+ }
+}
Oops, something went wrong.

0 comments on commit a2d2769

Please sign in to comment.