diff --git a/WorkOrder/WorkOrderCrud.cs b/WorkOrder/WorkOrderCrud.cs
new file mode 100644
index 0000000..915ff56
--- /dev/null
+++ b/WorkOrder/WorkOrderCrud.cs
@@ -0,0 +1,250 @@
+using System;
+using Xunit;
+using Newtonsoft.Json.Linq;
+using FluentAssertions;
+
+namespace raven_integration
+{
+
+ public class WorkOrderCrud
+ {
+
+ ///
+ /// Test all CRUD routes for a WorkOrder
+ ///
+ [Fact]
+ public async void CRUD()
+ {
+ //CREATE HEADER
+ dynamic d = new JObject();
+ d.active = true;
+ d.notes = "WOHEADER The quick brown fox jumped over the six lazy dogs!";
+ d.wiki = "# WOHEADER test wiki";
+
+ //Tags
+ dynamic dTagsArray = new JArray();
+ dTagsArray.Add("wo-Red");
+ dTagsArray.Add("wo-green");
+ dTagsArray.Add("wo-blue");
+ d.tags = dTagsArray;
+
+ ApiResponse a = await Util.PostAsync("workorders", await Util.GetTokenAsync("manager", "l3tm3in"), d.ToString());
+ Util.ValidateDataReturnResponseOk(a);
+ long WorkOrderId = a.ObjectResponse["data"]["id"].Value();
+
+
+ //CREATE WOITEM
+ d = new JObject();
+ d.active = true;
+ d.notes = "woitem The quick brown fox jumped over the six lazy dogs!";
+ d.wiki = "# woitem test wiki";
+ d.workOrderId = WorkOrderId;
+
+ //Tags
+ dTagsArray = new JArray();
+ dTagsArray.Add("woitem-Red");
+ dTagsArray.Add("woitem-green");
+ dTagsArray.Add("woitem-blue");
+ d.tags = dTagsArray;
+
+ a = await Util.PostAsync("workorders/items", await Util.GetTokenAsync("manager", "l3tm3in"), d.ToString());
+ Util.ValidateDataReturnResponseOk(a);
+ long WorkOrderItemId = a.ObjectResponse["data"]["id"].Value();
+
+ //CREATE WOITEMPART
+ d = new JObject();
+ d.active = true;
+ d.notes = "woitempart The quick brown fox jumped over the six lazy dogs!";
+ d.wiki = "# woitempart test wiki";
+ d.workOrderId = WorkOrderId;
+
+ //Tags
+ dTagsArray = new JArray();
+ dTagsArray.Add("woitempart-Red");
+ dTagsArray.Add("woitempart-green");
+ dTagsArray.Add("woitempart-blue");
+ d.tags = dTagsArray;
+
+ a = await Util.PostAsync("workorders/items/parts", await Util.GetTokenAsync("manager", "l3tm3in"), d.ToString());
+ Util.ValidateDataReturnResponseOk(a);
+ long WorkOrderItemPartId = a.ObjectResponse["data"]["id"].Value();
+
+
+ //RETRIEVE
+
+ //Get one
+ a = await Util.GetAsync("WorkOrder/" + WorkOrderId.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"));
+ Util.ValidateDataReturnResponseOk(a);
+ a.ObjectResponse["data"]["name"].Value().Should().Be(d.name.ToString());
+ a.ObjectResponse["data"]["notes"].Value().Should().Be(d.notes.ToString());
+ var returnedTags = ((JArray)a.ObjectResponse["data"]["tags"]);
+ returnedTags.Count.Should().Be(7);
+ returnedTags[0].Value().Should().Be("red-tag");
+ returnedTags[1].Value().Should().Be("orange-is-the-new-black");
+ returnedTags[2].Value().Should().Be("yellow");
+ returnedTags[3].Value().Should().Be("green");
+ returnedTags[4].Value().Should().Be("blue");
+ returnedTags[5].Value().Should().Be("indigo");
+ returnedTags[6].Value().Should().Be("violet-tag");
+
+
+
+ //UPDATE
+
+ d.name = Util.Uniquify("UPDATED VIA PUT TEST WorkOrder");
+
+ d.concurrencyToken = a.ObjectResponse["data"]["concurrencyToken"].Value();
+ a = await Util.PutAsync("WorkOrder/" + WorkOrderId.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), d.ToString());
+ Util.ValidateHTTPStatusCode(a, 200);
+
+ //check PUT worked
+ a = await Util.GetAsync("WorkOrder/" + WorkOrderId.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"));
+ Util.ValidateNoErrorInResponse(a);
+ a.ObjectResponse["data"]["name"].Value().Should().Be(d.name.ToString());
+ uint concurrencyToken = a.ObjectResponse["data"]["concurrencyToken"].Value();
+
+
+ //DELETE
+ a = await Util.DeleteAsync("WorkOrder/" + WorkOrderId.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"));
+ Util.ValidateHTTPStatusCode(a, 204);
+ }
+
+
+
+
+
+
+ ///
+ /// Test not found
+ ///
+ [Fact]
+ public async void GetNonExistentItemShouldError()
+ {
+ //Get non existant
+ //Should return status code 404, api error code 2010
+ ApiResponse a = await Util.GetAsync("WorkOrder/999999", await Util.GetTokenAsync("manager", "l3tm3in"));
+ Util.ValidateResponseNotFound(a);
+ }
+
+ ///
+ /// Test bad modelstate
+ ///
+ [Fact]
+ public async void GetBadModelStateShouldError()
+ {
+ //Get non existant
+ //Should return status code 400, api error code 2200 and a first target in details of "id"
+ ApiResponse a = await Util.GetAsync("WorkOrder/2q2", await Util.GetTokenAsync("manager", "l3tm3in"));
+ Util.ValidateBadModelStateResponse(a, "id");
+ }
+
+
+ ///
+ /// Test server exception
+ ///
+ [Fact]
+ public async void ServerExceptionShouldErrorPropertly()
+ {
+ //Get non existant
+ //Should return status code 400, api error code 2200 and a first target in details of "id"
+ ApiResponse a = await Util.GetAsync("WorkOrder/exception", await Util.GetTokenAsync("manager", "l3tm3in"));
+ Util.ValidateServerExceptionResponse(a);
+ }
+
+
+
+ ///
+ /// Test server alt exception
+ ///
+ [Fact]
+ public async void ServerAltExceptionShouldErrorPropertly()
+ {
+ //Get non existant
+ //Should return status code 400, api error code 2200 and a first target in details of "id"
+ ApiResponse a = await Util.GetAsync("WorkOrder/altexception", await Util.GetTokenAsync("manager", "l3tm3in"));
+ Util.ValidateServerExceptionResponse(a);
+ }
+
+
+
+
+ ///
+ ///
+ ///
+ [Fact]
+ public async void PutConcurrencyViolationShouldFail()
+ {
+
+ //CREATE
+
+ dynamic w2 = new JObject();
+ w2.name = Util.Uniquify("PutConcurrencyViolationShouldFail");
+ w2.dollarAmount = 2.22m;
+ w2.active = true;
+ w2.usertype = 1;
+
+ w2.notes = "blah";
+ w2.customFields = Util.WorkOrderRequiredCustomFieldsJsonString();
+
+
+ ApiResponse r2 = await Util.PostAsync("WorkOrder", await Util.GetTokenAsync("manager", "l3tm3in"), w2.ToString());
+ Util.ValidateDataReturnResponseOk(r2);
+ long w2Id = r2.ObjectResponse["data"]["id"].Value();
+ uint OriginalConcurrencyToken = r2.ObjectResponse["data"]["concurrencyToken"].Value();
+
+
+
+ //UPDATE
+ //PUT
+
+ w2.name = Util.Uniquify("PutConcurrencyViolationShouldFail UPDATE VIA PUT ");
+
+ w2.concurrencyToken = OriginalConcurrencyToken - 1;//bad token
+ ApiResponse PUTTestResponse = await Util.PutAsync("WorkOrder/" + w2Id.ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), w2.ToString());
+ Util.ValidateConcurrencyError(PUTTestResponse);
+
+
+ }
+
+
+
+
+ ///
+ ///
+ ///
+ [Fact]
+ public async void PatchConcurrencyViolationShouldFail()
+ {
+
+ //CREATE
+
+ dynamic w2 = new JObject();
+ w2.name = Util.Uniquify("PatchConcurrencyViolationShouldFail");
+ w2.notes = "blah";
+ w2.customFields = Util.WorkOrderRequiredCustomFieldsJsonString();
+ w2.dollarAmount = 2.22m;
+ w2.active = true;
+ w2.usertype = 1;
+ w2.customFields = Util.WorkOrderRequiredCustomFieldsJsonString();
+
+ ApiResponse r2 = await Util.PostAsync("WorkOrder", await Util.GetTokenAsync("manager", "l3tm3in"), w2.ToString());
+ Util.ValidateDataReturnResponseOk(r2);
+ long w2Id = r2.ObjectResponse["data"]["id"].Value();
+ uint OriginalConcurrencyToken = r2.ObjectResponse["data"]["concurrencyToken"].Value();
+
+
+ //PATCH
+ var newName = Util.Uniquify("PutConcurrencyViolationShouldFail UPDATED VIA PATCH");
+ string patchJson = "[{\"value\": \"" + newName + "\",\"path\": \"/name\",\"op\": \"replace\"}]";
+ ApiResponse PATCHTestResponse = await Util.PatchAsync("WorkOrder/" + w2Id.ToString() + "/" + (OriginalConcurrencyToken - 1).ToString(), await Util.GetTokenAsync("manager", "l3tm3in"), patchJson);
+ Util.ValidateConcurrencyError(PATCHTestResponse);
+ }
+
+
+
+
+
+ //==================================================
+
+ }//eoc
+}//eons