Skip to content
This repository has been archived by the owner on Oct 6, 2022. It is now read-only.

Commit

Permalink
Merge pull request #4 from christriddle/master
Browse files Browse the repository at this point in the history
Supports multi feature toggles. Removed etcetera dependency.
  • Loading branch information
ryantomlinson committed Nov 26, 2014
2 parents 8ad7f0e + e8ba725 commit b96acc5
Show file tree
Hide file tree
Showing 38 changed files with 336 additions and 938 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,5 @@ FakesAssemblies/

!Build/nunit/bin/
!Build/nunit/bin/*

*.userprefs
84 changes: 84 additions & 0 deletions HobknobClientNet.Tests/EtcdClientForTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Net;
using System.Text;

namespace HobknobClientNet.Tests
{
internal class EtcdClientForTests : EtcdClient
{
public EtcdClientForTests(Uri keysUri) : base(keysUri)
{
}

public void Set(Uri relativeKeyUri, string value)
{
byte[] bytes = Encoding.UTF8.GetBytes("value=" + value);

var webRequest = (HttpWebRequest)WebRequest.Create(GetFullUri(relativeKeyUri));
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.Method = "PUT";
webRequest.ContentLength = bytes.Length;

using (var dataStream = webRequest.GetRequestStream())
{
dataStream.Write(bytes, 0, bytes.Length);
dataStream.Flush();
using (webRequest.GetResponse())
{
}
}
}

public void DeleteDir(Uri relativeKeyUri)
{
if (!relativeKeyUri.OriginalString.EndsWith("/"))
{
throw new ArgumentException("Use Delete to delete a key, or end the key with a forward slash.to delete a directory.");
}
var fullKeyUriWithQuery = new UriBuilder(GetFullUri(relativeKeyUri)) { Query = "recursive=true" };

var webRequest = (HttpWebRequest)WebRequest.Create(fullKeyUriWithQuery.Uri);
webRequest.Method = "DELETE";

try
{
using (webRequest.GetResponse())
{
}
}
catch (WebException ex)
{
if (((HttpWebResponse)ex.Response).StatusCode == HttpStatusCode.NotFound)
{
return;
}
throw;
}
}

public void Delete(Uri relativeKeyUri)
{
if (relativeKeyUri.OriginalString.EndsWith("/"))
{
throw new ArgumentException("Use DeleteDir to delete a directory, or don't end the key with a forward slash.");
}

var webRequest = (HttpWebRequest)WebRequest.Create(GetFullUri(relativeKeyUri));
webRequest.Method = "DELETE";
try
{
using (webRequest.GetResponse())
{
}
}
catch (WebException ex)
{
if (((HttpWebResponse) ex.Response).StatusCode == HttpStatusCode.NotFound)
{
return;
}
throw;
}
}
}
}
9 changes: 3 additions & 6 deletions HobknobClientNet.Tests/HobknobClientNet.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,14 @@
<Reference Include="System.Core" />
</ItemGroup>
<ItemGroup>
<Compile Include="EtcdClientForTests.cs" />
<Compile Include="Scenarios\CacheUpdates.cs" />
<Compile Include="Scenarios\GetOrDefault.cs" />
<Compile Include="Scenarios\Get.cs" />
<Compile Include="Scenarios\GetOrDefault_MultiFeatureToggle.cs" />
<Compile Include="Scenarios\GetOrDefault_SimpleFeature.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Scenarios\TestBase.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\HobknobClientNet.etcetera\HobknobClientNet.etcetera.csproj">
<Project>{fdd68d70-0708-427f-a3f3-7296cdb8cb52}</Project>
<Name>HobknobClientNet.etcetera</Name>
</ProjectReference>
<ProjectReference Include="..\HobknobClientNet\HobknobClientNet.csproj">
<Project>{0aba6365-c6bb-4596-9782-281994eb3256}</Project>
<Name>HobknobClientNet</Name>
Expand Down
37 changes: 19 additions & 18 deletions HobknobClientNet.Tests/Scenarios/CacheUpdates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,64 +7,65 @@
namespace HobknobClientNet.Tests.Scenarios
{
[TestFixture]
public class CacheUpdates : TestBase
internal class CacheUpdates : TestBase
{
private const string ApplicationName = "cacheUpdateTest";
bool? _toggleValue;

[SetUp]
public void SetUp()
{
TearDown();
Set_cache_update_interval(TimeSpan.FromSeconds(1));
Set_application_name("cacheUpdateTest");
Set_application_name(ApplicationName);
_toggleValue = null;
}

[TearDown]
public void TearDown()
{
EtcdClient.DeleteDir("v1/toggles/cacheUpdateTest", true);
EtcdClient.DeleteDir(new Uri("v1/toggles/cacheUpdateTest/", UriKind.Relative));
}

[Test]
public void Cache_is_not_updated_when_update_interval_is_not_passed()
{
Given_a_toggle("cacheUpdateTest", "toggle1", "true");
When_I_get("toggle1", out _toggleValue);
Given_a_toggle(ApplicationName, "feature1", "true");
When_I_get_with_default("feature1", false, out _toggleValue);
Assert.That(_toggleValue, Is.True);

Given_a_toggle("cacheUpdateTest", "toggle1", "false");
Given_a_toggle(ApplicationName, "feature1", "false");

When_I_get_without_initialising_a_new_hobknob_instance("toggle1", out _toggleValue);
When_I_get_with_default_without_initialising_a_new_hobknob_instance("feature1", out _toggleValue);
Assert.That(_toggleValue, Is.True);
}

[Test]
public void Cache_is_updated_when_update_interval_is_passed()
{
Given_a_toggle("cacheUpdateTest", "toggle1", "true");
When_I_get("toggle1", out _toggleValue);
Given_a_toggle(ApplicationName, "feature1", "true");
When_I_get_with_default("feature1", false, out _toggleValue);
Assert.That(_toggleValue, Is.True);

Given_a_toggle("cacheUpdateTest", "toggle1", "false");
Given_a_toggle(ApplicationName, "feature1", "false");
Thread.Sleep(TimeSpan.FromSeconds(1.2));

When_I_get_without_initialising_a_new_hobknob_instance("toggle1", out _toggleValue);
When_I_get_with_default_without_initialising_a_new_hobknob_instance("feature1", out _toggleValue);
Assert.That(_toggleValue, Is.False);
}

[Test]
public void Cache_updated_information_is_correct()
{
Given_a_toggle("cacheUpdateTest", "existingNoChange", "true");
Given_a_toggle("cacheUpdateTest", "existingChange", "true");
Given_a_toggle("cacheUpdateTest", "existingRemoved", "true");
Given_a_toggle(ApplicationName, "existingNoChange", "true");
Given_a_toggle(ApplicationName, "existingChange", "true");
Given_a_toggle(ApplicationName, "existingRemoved", "true");

var hobknobClient = Create_hobknob_client();

Given_a_toggle("cacheUpdateTest", "newToggle", "true");
Given_a_toggle("cacheUpdateTest", "existingChange", "false");
Given_a_toggle_is_removed("cacheUpdateTest", "existingRemoved");
Given_a_toggle(ApplicationName, "newToggle", "true");
Given_a_toggle(ApplicationName, "existingChange", "false");
Given_a_toggle_is_removed(ApplicationName, "existingRemoved");

CacheUpdatedArgs cacheUpdatedArgs = null;
hobknobClient.CacheUpdated += (sender, args) =>
Expand All @@ -88,7 +89,7 @@ public void Cache_updated_information_is_correct()

private void AssertToggleUpdate(Dictionary<string, CacheUpdate> updates, string key, bool? oldValue, bool? newValue)
{
var cacheUpdate = updates[key];
var cacheUpdate = updates["/v1/toggles/" + ApplicationName + "/" + key];
Assert.That(cacheUpdate.OldValue, Is.EqualTo(oldValue));
Assert.That(cacheUpdate.NewValue, Is.EqualTo(newValue));
}
Expand Down
55 changes: 0 additions & 55 deletions HobknobClientNet.Tests/Scenarios/Get.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using NUnit.Framework;

namespace HobknobClientNet.Tests.Scenarios
{
[TestFixture]
internal class GetOrDefault_MultiFeatureToggle : TestBase
{
bool? _toggleValue;

[SetUp]
public void SetUp()
{
Set_application_name("app1");
_toggleValue = null;
}

[Test]
public void Default_not_used_when_fature_and_toggle_exists()
{
Given_a_toggle("app1", "feature1", "toggle1", "true");
When_I_get_with_default("feature1", "toggle1", false, out _toggleValue);
Assert.That(_toggleValue, Is.True);
}

[Test]
public void Default_is_used_when_feature_does_not_exist()
{
Given_a_toggle("app1", "feature1", "toggle1", "true");
When_I_get_with_default("feature3", "toggle1", true, out _toggleValue);
Assert.That(_toggleValue, Is.True);
}

[Test]
public void Default_is_used_when_toggle_does_not_exist()
{
Given_a_toggle("app1", "feature1", "toggle1", "true");
When_I_get_with_default("feature1", "toggle3", true, out _toggleValue);
Assert.That(_toggleValue, Is.True);
}

[Test]
public void Applications_do_not_clash()
{
Given_a_toggle("app1", "feature1", "toggle1", "true");
Given_a_toggle("app2", "feature1", "toggle1", "false");
When_I_get_with_default("feature1", "toggle1", false, out _toggleValue);
Assert.That(_toggleValue, Is.True);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace HobknobClientNet.Tests.Scenarios
{
[TestFixture]
public class GetOrDefault : TestBase
internal class GetOrDefault_SimpleFeature : TestBase
{
bool? _toggleValue;

Expand All @@ -17,24 +17,24 @@ public void SetUp()
[Test]
public void Default_not_used_when_key_exists()
{
Given_a_toggle("app1", "toggle1", "true");
When_I_get_with_default("toggle1", false, out _toggleValue);
Given_a_toggle("app1", "feature1", "true");
When_I_get_with_default("feature1", false, out _toggleValue);
Assert.That(_toggleValue, Is.True);
}

[Test]
public void Default_is_used_when_key_does_not_exist()
{
When_I_get_with_default("toggle3", true, out _toggleValue);
When_I_get_with_default("feature3", true, out _toggleValue);
Assert.That(_toggleValue, Is.True);
}

[Test]
public void Applications_do_not_clash()
{
Given_a_toggle("app1", "toggle1", "true");
Given_a_toggle("app2", "toggle1", "false");
When_I_get_with_default("toggle1", false, out _toggleValue);
Given_a_toggle("app1", "feature1", "true");
Given_a_toggle("app2", "feature1", "false");
When_I_get_with_default("feature1", false, out _toggleValue);
Assert.That(_toggleValue, Is.True);
}
}
Expand Down
Loading

0 comments on commit b96acc5

Please sign in to comment.