Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: bettiolo/PassFruit
base: d5aacb61d3
...
head fork: bettiolo/PassFruit
compare: 6a02ddf0fc
Checking mergeability… Don't worry, you can still create the pull request.
  • 4 commits
  • 20 files changed
  • 0 commit comments
  • 1 contributor
View
4 src/PassFruit.Client.InMemoryRepository.Tests/InMemoryRepositoryTests.cs
@@ -12,11 +12,11 @@
namespace PassFruit.Client.InMemoryRepository.Tests {
[TestFixture]
- public class InMemoryRepositoryTests : RepositoryTests {
+ public class InMemoryRepositoryTests : RepositoryTestsBase {
private readonly InMemoryRepositoryConfiguration _configuration = new InMemoryRepositoryConfiguration(Path.GetTempFileName());
- protected override IRepository GetRepositoryWithFakeData() {
+ protected override IRepository GetNewRepositoryWithFakeData() {
var repository = new InMemoryRepository(_configuration);
var fakeDataGenerator = new FakeDataGenerator();
fakeDataGenerator.GenerateFakeData(repository);
View
27 src/PassFruit.Client.InMemoryRepository/InMemoryRepository.cs
@@ -16,15 +16,18 @@ public class InMemoryRepository : RepositoryBase {
get { return (InMemoryRepositoryConfiguration)base.Configuration; }
}
- private readonly Dictionary<Guid, Dictionary<string, string>> _passwords =
+ private readonly Dictionary<Guid, Dictionary<string, string>> _passwords =
new Dictionary<Guid, Dictionary<string, string>>();
+ private readonly Dictionary<Guid, IAccount> _accounts =
+ new Dictionary<Guid, IAccount>();
+
public override string Name {
get { return "In Memory Repository"; }
}
public override string Description {
- get { return "In Memory repository, the data is not persisted anywhere"; }
+ get { return "In Memory repository, the data is serialized"; }
}
public override string GetPassword(Guid accountId, string passwordKey = DefaultPasswordKey) {
@@ -53,20 +56,28 @@ public class InMemoryRepository : RepositoryBase {
_passwords[accountId][passwordKey] = password;
}
+ public override void DeletePasswords(Guid accountId) {
+ _passwords.Remove(accountId);
+ }
+
protected override void InternalSave(IAccount account) {
- throw new NotImplementedException();
+ _accounts[account.Id] = account;
+ }
+
+ protected override IEnumerable<Guid> GetAllAccountIds() {
+ return _accounts.Select(item => item.Key);
}
- protected override IAccount GetAccount(Guid accountId) {
- throw new NotImplementedException();
+ protected override IEnumerable<Guid> GetDeletedAccountIds() {
+ return _accounts.Where(item => item.Value is DeletedAccount).Select(account => account.Key);
}
- protected override IEnumerable<Guid> GetAllAccountIds(bool includingDeleted = false) {
- throw new NotImplementedException();
+ protected override IAccount LoadAccount(Guid accountId) {
+ return _accounts[accountId];
}
protected override void LoadAllFieldTypes() {
- throw new NotImplementedException();
+ // ?
}
}
View
11 src/PassFruit.Client.XmlRepository.Tests/XmlRepositoryTests.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using FluentAssertions;
@@ -10,11 +11,15 @@
namespace PassFruit.Client.XmlRepository.Tests {
[TestFixture]
- public class XmlRepositoryTests : RepositoryTests {
+ public class XmlRepositoryTests : RepositoryTestsBase {
- private readonly XmlRepositoryConfiguration _configuration = new XmlRepositoryConfiguration(Path.GetTempFileName());
+ private XmlRepositoryConfiguration _configuration;
- protected override IRepository GetRepositoryWithFakeData() {
+ protected override IRepository GetNewRepositoryWithFakeData() {
+ _configuration = new XmlRepositoryConfiguration(Path.GetTempFileName());
+ if (File.Exists(_configuration.XmlFilePath)) {
+ File.Delete(_configuration.XmlFilePath);
+ }
var repository = new XmlRepository(_configuration);
var fakeDataGenerator = new FakeDataGenerator();
fakeDataGenerator.GenerateFakeData(repository);
View
110 src/PassFruit.Client.XmlRepository/XmlRepository.cs
@@ -11,16 +11,28 @@ public class XmlRepository : RepositoryBase {
/* <passfruit>
* <accounts>
- * <0000-0000-0000-0000>
- * <username>user name</username>
- * <email>info@example.org</email>
- * </0000-0000-0000-0000>
+ * <id-0000-0000-0000-0000>
+ * <provider>
+ * provider name
+ * </provider>
+ * <fields>
+ * <username>user name</username>
+ * <email>info@example.org</email>
+ * </fields>
+ * <tags>
+ * <tag-key />
+ * </tags>
+ * <note>
+ * bla bla bla
+ * </note>
+ * </id-0000-0000-0000-0000>
* </accounts>
* <passwords>
- * <0000-0000-0000-0000>
+ * <id-0000-0000-0000-0000>
* <default>
- * ENCRYPTED DATA
+ * passwo0rd1
* </default>
+ * </id-0000-0000-0000-0000>
* </passwords>
* </passfruit>
*/
@@ -34,6 +46,7 @@ public class XmlRepository : RepositoryBase {
}
private const string AccountIdPrefix = "id-";
+
private const string TagPrefix = "tag-";
private XDocument _xDoc;
@@ -58,34 +71,53 @@ public class XmlRepository : RepositoryBase {
passwordKey = DefaultPasswordKey;
}
GetPasswordElement(accountId, passwordKey).Value = password;
- SaveXml();
}
- protected override IEnumerable<Guid> GetAllAccountIds(bool includingDeleted = false) {
+ protected override IEnumerable<Guid> GetAllAccountIds() {
+ return GetAccountIdsWithFilter(accountId => !AccountElementIsDeleted(accountId));
+ }
+
+ protected override IEnumerable<Guid> GetDeletedAccountIds() {
+ return GetAccountIdsWithFilter(AccountElementIsDeleted);
+ }
+ private bool AccountElementIsDeleted(Guid accountId) {
+ return GetAccountDeletedElement(accountId).Value == bool.TrueString;
+ }
+
+ private IEnumerable<Guid> GetAccountIdsWithFilter(Func<Guid, bool> filterAccount) {
var accountsElement = GetAccountsElement();
var accountIds = new List<Guid>();
- foreach (var element in accountsElement.Elements()) {
- var accountId = element.Name.LocalName;
- accountId = accountId.Remove(0, AccountIdPrefix.Length);
+ foreach (var accountElement in accountsElement.Elements()) {
+ var accountElementName = accountElement.Name.LocalName;
+ if (!accountElementName.StartsWith(AccountIdPrefix)) {
+ throw new Exception(string.Format("The account id '{0}' is not starting with the prefix '{1}'", accountElementName, AccountIdPrefix));
+ }
+ var accountId = accountElementName.Remove(0, AccountIdPrefix.Length);
Guid guid;
- if (Guid.TryParse(accountId, out guid)) {
- accountIds.Add(guid);
+ if (Guid.TryParse(accountId, out guid) && filterAccount(guid)) {
+ accountIds.Add(guid);
}
}
return accountIds;
}
- protected override IAccount GetAccount(Guid accountId) {
- var accountFieldsElement = GetAccountFieldsElement(accountId);
- IAccount account = null;
- foreach (var fieldElement in accountFieldsElement.Elements()) {
- if (account == null) {
- account = Accounts.Create(GetProviderFieldElement(accountId).Value, accountId);
- }
- var fieldTypeKey = (FieldTypeKey)Enum.Parse(typeof (FieldTypeKey), fieldElement.Name.LocalName, true);
+ protected override IAccount LoadAccount(Guid accountId) {
+ var account = Accounts.Create(GetProviderFieldElement(accountId).Value, accountId);
+ foreach (var fieldElement in GetAccountFieldsElement(account.Id).Elements()) {
+ var fieldElementName = fieldElement.Name.LocalName;
+ var fieldTypeKey = (FieldTypeKey)Enum.Parse(typeof (FieldTypeKey), fieldElementName, true);
account.SetField(fieldTypeKey, fieldElement.Value);
- account.SetClean();
}
+ foreach (var tagElement in GetTagsElement(account.Id).Elements()) {
+ var tagElementName = tagElement.Name.LocalName;
+ if (!tagElementName.StartsWith(TagPrefix)) {
+ throw new Exception(string.Format("The tag name '{0}' is not starting with the prefix '{1}'", tagElementName, TagPrefix));
+ }
+ var tagKey = tagElement.Name.LocalName.Remove(0, TagPrefix.Length);
+ account.Tags.Add(tagKey);
+ }
+ account.Notes = GetNoteElement(account.Id).Value;
+ account.SetClean();
return account;
}
@@ -135,15 +167,23 @@ public class XmlRepository : RepositoryBase {
return GetOrCreateElement("provider", GetAccountElement(accountId));
}
- private XElement GetTagsElement() {
- return GetOrCreateElement("tags", GetPassfruitElement());
+ private XElement GetAccountDeletedElement(Guid accountId) {
+ return GetOrCreateElement("deleted", GetAccountElement(accountId));
+ }
+
+ private XElement GetTagsElement(Guid accountId) {
+ return GetOrCreateElement("tags", GetAccountElement(accountId));
+ }
+
+ private XElement GetNoteElement(Guid accountId) {
+ return GetOrCreateElement("note", GetAccountElement(accountId));
}
- private XElement GetTagElement(string tagName) {
- return GetOrCreateElement(TagPrefix + tagName, GetPassfruitElement());
+ private XElement GetTagElement(string tagName, Guid accountId) {
+ return GetOrCreateElement(TagPrefix + tagName, GetTagsElement(accountId));
}
- private XElement GetAccountField(IField field, Guid accountId) {
+ private XElement GetAccountFieldElement(IField field, Guid accountId) {
return GetOrCreateElement(field.FieldType.Key.ToString(), GetAccountFieldsElement(accountId));
}
@@ -157,22 +197,26 @@ public class XmlRepository : RepositoryBase {
return element;
}
- private void SaveXml() {
- _xDoc.Save(Configuration.XmlFilePath);
+ public override void DeletePasswords(Guid accountId) {
+ GetAccountPasswordsElement(accountId).Remove();
}
protected override void InternalSave(IAccount account) {
+ GetAccountElement(account.Id).Remove();
if (account.Provider != null) {
GetProviderFieldElement(account.Id).Value = account.Provider.Key;
}
foreach (var field in account.Fields) {
- GetAccountField(field, account.Id).Value = field.Value.ToString();
+ GetAccountFieldElement(field, account.Id).Value = field.Value.ToString();
}
foreach (var tag in account.Tags) {
- // var tagElement = GetTagElement(tag.Key);
- // ToDo: Should handle adding, removing and changing tags
+ GetTagElement(tag.Key, account.Id);
}
- SaveXml();
+ if (account is DeletedAccount) {
+ GetAccountDeletedElement(account.Id).Value = bool.TrueString;
+ }
+ GetNoteElement(account.Id).Value = account.Notes ?? "";
+ _xDoc.Save(Configuration.XmlFilePath);
}
}
View
6 src/PassFruit.Contracts/IAccount.cs
@@ -19,7 +19,9 @@ public interface IAccount {
void SetPassword(string password, string passwordKey = "");
- IEnumerable<ITag> Tags { get; }
+ void DeleteAllPasswords();
+
+ ITags Tags { get; }
IEnumerable<IField> Fields { get; }
@@ -35,8 +37,6 @@ public interface IAccount {
void SetField(FieldTypeKey fieldTypeKey, object value);
- void AddTag(string tagName);
-
}
}
View
13 src/PassFruit.Contracts/IRepository.cs
@@ -11,17 +11,19 @@ public interface IRepository {
string Description { get; }
+ IRepositoryConfiguration Configuration { get; }
+
IAccounts Accounts { get; }
- ITags Tags { get; }
+ IEnumerable<ITag> GetAllTags();
IProviders Providers { get; }
IFieldTypes FieldTypes { get; }
- string GetPassword(Guid accountId, string passwordKey);
+ string GetPassword(Guid accountId, string passwordKey = null);
- void SetPassword(Guid accountId, string password, string passwordKey);
+ void SetPassword(Guid accountId, string password, string passwordKey = null);
event EventHandler<RepositorySaveEventArgs> OnSaved;
@@ -32,5 +34,10 @@ public interface IRepository {
void Save(IAccount account);
bool IsDirty();
+
+ void DeletePasswords(Guid accountId);
+
+ IEnumerable<IAccount> GetDeletedAccounts();
+
}
}
View
2  src/PassFruit.Contracts/ITag.cs
@@ -7,7 +7,7 @@ namespace PassFruit.Contracts {
public interface ITag {
- string Key { get; set; }
+ string Key { get; }
IEnumerable<IAccount> Accounts { get; }
View
6 src/PassFruit.Contracts/ITags.cs
@@ -9,11 +9,11 @@ public interface ITags : IEnumerable<ITag> {
ITag this[string key] { get; }
- IEnumerable<ITag> GetByAccountId(Guid accountId);
-
bool Contains(string key);
- ITag Create(string key);
+ void Add(string key);
+
+ void Remove(string key);
}
View
31 src/PassFruit.Tests.FakeData/FakeDataGenerator.cs
@@ -5,6 +5,10 @@ namespace PassFruit.Tests.FakeData {
public class FakeDataGenerator {
+ public const string GenericProviderKey = "generic";
+ public const string FacebookProviderKey = "facebook";
+ public const string TwitterProviderKey = "twitter";
+ public const string GoogleProviderKey = "google";
public const string FacebookEmail = @"testFacebook@example.com";
public const string FacebookPassword = @"Password1";
public const string TwitterEmail = @"testTwitter@example.com";
@@ -14,37 +18,40 @@ public class FakeDataGenerator {
public const string Goggle1Password = @"Password3";
public const string Google2Email = @"example2@gmail.com";
public const string Google2Password = @"Password4";
+ public const string Tag1 = @"Tag-1";
+ public const string Tag2 = @"Tag-2";
+ public const string Tag3 = @"Tag-3";
public void GenerateFakeData(IRepository repository) {
- var facebookAccount = repository.Accounts.Create("facebook");
+ var facebookAccount = repository.Accounts.Create(FacebookProviderKey);
facebookAccount.Notes = @"Example test note 1";
facebookAccount.SetField(FieldTypeKey.Email, FacebookEmail);
- facebookAccount.AddTag("Tag 1");
- facebookAccount.AddTag("Tag 2");
+ facebookAccount.Tags.Add(Tag1);
+ facebookAccount.Tags.Add(Tag2);
facebookAccount.SetPassword(FacebookPassword);
repository.Accounts.Add(facebookAccount);
- var twitterAccount = repository.Accounts.Create("twitter");
+ var twitterAccount = repository.Accounts.Create(TwitterProviderKey);
twitterAccount.Notes = @"Example test note 2 blah blah blah\nBlah blah blah\nLorem ipsun dolor sit amet";
twitterAccount.SetField(FieldTypeKey.Email, TwitterEmail);
twitterAccount.SetField(FieldTypeKey.UserName, TwitterUserName);
- twitterAccount.AddTag("Tag 1");
+ twitterAccount.Tags.Add(Tag1);
twitterAccount.SetPassword(TwitterPassword);
repository.Accounts.Add(twitterAccount);
- var googleAccount = repository.Accounts.Create("google");
+ var googleAccount = repository.Accounts.Create(GoogleProviderKey);
googleAccount.SetField(FieldTypeKey.Email, Google1Email);
- googleAccount.AddTag("Tag 1");
- googleAccount.AddTag("Tag 2");
- googleAccount.AddTag("Tag 3");
+ googleAccount.Tags.Add(Tag1);
+ googleAccount.Tags.Add(Tag2);
+ googleAccount.Tags.Add(Tag3);
googleAccount.SetPassword(Goggle1Password);
repository.Accounts.Add(googleAccount);
- var googleAccount2 = repository.Accounts.Create("google");
+ var googleAccount2 = repository.Accounts.Create(GoogleProviderKey);
googleAccount2.SetField(FieldTypeKey.Email, Google2Email);
- googleAccount2.AddTag("Tag 2");
- googleAccount2.AddTag("Tag 3");
+ googleAccount2.Tags.Add(Tag2);
+ googleAccount2.Tags.Add(Tag3);
googleAccount2.SetPassword(Google2Password);
repository.Accounts.Add(googleAccount2);
View
2  src/PassFruit.Tests/PassFruit.Tests.csproj
@@ -48,7 +48,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
- <Compile Include="RepositoryTests.cs" />
+ <Compile Include="RepositoryTestsBase.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
View
183 src/PassFruit.Tests/RepositoryTests.cs → src/PassFruit.Tests/RepositoryTestsBase.cs
@@ -8,19 +8,27 @@
namespace PassFruit.Tests {
[TestFixture]
- public abstract class RepositoryTests {
+ public abstract class RepositoryTestsBase {
- protected abstract IRepository GetRepositoryWithFakeData();
+ protected abstract IRepository GetNewRepositoryWithFakeData();
protected abstract IRepository GetReloadedRepository();
+ private void TestWithUnsavedFakeData(Action<IRepository> test) {
+ test(GetNewRepositoryWithFakeData());
+ }
+
private void TestWithReloadedRepository(Action<IRepository> test) {
- GetRepositoryWithFakeData().SaveAll();
+ test(GetReloadedRepository());
+ }
+
+ private void TestWithPrepopulatedReloadedRepository(Action<IRepository> test) {
+ GetNewRepositoryWithFakeData().SaveAll();
test(GetReloadedRepository());
}
private void TestWithBothRepositories(Action<IRepository> test) {
- test(GetRepositoryWithFakeData());
+ test(GetNewRepositoryWithFakeData());
test(GetReloadedRepository());
}
@@ -47,28 +55,28 @@ public abstract class RepositoryTests {
// Then
TestWithBothRepositories(repository => {
-
+
actLoadAccounts(repository);
facebookAccount.Should().NotBeNull();
facebookAccount.GetDefaultField(FieldTypeKey.Email).Value.Should().Be(testFacebookEmail);
facebookAccount.Provider.Should().NotBeNull();
- facebookAccount.Provider.Key.Should().Be("facebook");
+ facebookAccount.Provider.Key.Should().Be(FakeDataGenerator.FacebookProviderKey);
twitterAccountByUserName.Should().NotBeNull();
twitterAccountByUserName.GetDefaultField(FieldTypeKey.UserName).Value.Should().Be(testTwitterAccount);
twitterAccountByUserName.Provider.Should().NotBeNull();
- twitterAccountByUserName.Provider.Key.Should().Be("twitter");
+ twitterAccountByUserName.Provider.Key.Should().Be(FakeDataGenerator.TwitterProviderKey);
twitterAccountByEmail.Should().NotBeNull();
twitterAccountByEmail.GetDefaultField(FieldTypeKey.Email).Value.Should().Be(testTwitterEmail);
twitterAccountByEmail.Provider.Should().NotBeNull();
- twitterAccountByEmail.Provider.Key.Should().Be("twitter");
+ twitterAccountByEmail.Provider.Key.Should().Be(FakeDataGenerator.TwitterProviderKey);
gmailAccount.Should().NotBeNull();
gmailAccount.GetDefaultField(FieldTypeKey.Email).Value.Should().Be(testGoogleEmail);
gmailAccount.Provider.Should().NotBeNull();
- gmailAccount.Provider.Key.Should().Be("google");
+ gmailAccount.Provider.Key.Should().Be(FakeDataGenerator.GoogleProviderKey);
});
@@ -78,19 +86,20 @@ public abstract class RepositoryTests {
public void When_An_Account_Is_Added_Then_The_Number_Of_Accounts_Should_Increase() {
// Given
+ const string newEmail = "testFacebookTemp@tin.it";
var originalAccountCount = 0;
IAccount facebookAccount = null;
// When
Action<IRepository> actLoadAccount = repository => {
originalAccountCount = repository.Accounts.Count();
- facebookAccount = repository.Accounts.Create("generic");
+ facebookAccount = repository.Accounts.Create(FakeDataGenerator.GenericProviderKey);
};
- Action<IRepository> actEditAccount = repository =>
- facebookAccount.SetField(FieldTypeKey.Email, "testFacebookTemp@tin.it");
- Action<IRepository> actAddAccount = repository =>
+ Action<IRepository> actEditAccount = repository =>
+ facebookAccount.SetField(FieldTypeKey.Email, newEmail);
+ Action<IRepository> actAddAccount = repository =>
repository.Accounts.Add(facebookAccount);
- Action<IRepository> actRepositorySaveAll = repository =>
+ Action<IRepository> actRepositorySaveAll = repository =>
repository.SaveAll();
// Then
@@ -113,11 +122,13 @@ public abstract class RepositoryTests {
var originalAccountCount = 0;
IAccount facebookAccount = null;
IAccount deletedAccount = null;
+ var facebookAccountId = Guid.Empty;
// When
Action<IRepository> actLoadAccount = repository => {
originalAccountCount = repository.Accounts.Count();
facebookAccount = repository.Accounts.GetByEmail(FakeDataGenerator.FacebookEmail).First();
+ facebookAccountId = facebookAccount.Id;
};
Action<IRepository> actRemoveAccount = repository =>
repository.Accounts.Remove(facebookAccount);
@@ -125,7 +136,7 @@ public abstract class RepositoryTests {
deletedAccount = repository.Accounts.First(a => a is DeletedAccount);
// Then
- TestWithReloadedRepository(repository => {
+ TestWithPrepopulatedReloadedRepository(repository => {
actLoadAccount(repository);
for (int i = 0; i < 3; i++) {
if (i < 2) {
@@ -140,24 +151,26 @@ public abstract class RepositoryTests {
repository.Accounts.First(a => a is DeletedAccount).IsDirty.Should().BeTrue();
repository.IsDirty().Should().BeTrue();
}
+ repository.GetPassword(facebookAccount.Id).Should().BeBlank();
+ repository.IsDirty().Should().BeTrue();
repository.SaveAll();
+ facebookAccount.GetPassword().Should().BeBlank();
+ repository.GetPassword(facebookAccount.Id).Should().BeBlank();
repository.IsDirty().Should().BeFalse();
repository.Accounts.GetByEmail(FakeDataGenerator.FacebookEmail).Should().BeEmpty();
repository.Accounts.Count().Should().Be(originalAccountCount - 1);
repository.Accounts.Should().NotContain(a => a is DeletedAccount);
+ });
- // check password
- throw new NotImplementedException();
- });
TestWithReloadedRepository(repository => {
repository.IsDirty().Should().BeFalse();
repository.Accounts.GetByEmail(FakeDataGenerator.FacebookEmail).Should().BeEmpty();
repository.Accounts.Count().Should().Be(originalAccountCount - 1);
-
- // check password
-
- throw new NotImplementedException();
+ repository.GetPassword(facebookAccountId).Should().BeBlank();
+ var deletedAccounts = repository.GetDeletedAccounts();
+ deletedAccounts.Any().Should().BeTrue();
+ deletedAccounts.Should().Contain(account => account.Id == facebookAccountId);
});
}
@@ -166,23 +179,33 @@ public abstract class RepositoryTests {
public void When_An_Account_Is_Edited_Then_The_Correct_Data_Should_Be_Retrieved() {
// Given
- var editedUser = "edItedUsEr";
- var editedEmail = "editedEmail@twitter.example";
- var repository = GetRepositoryWithFakeData();
- var accountWithUserName = repository.Accounts.GetByUserName(FakeDataGenerator.TwitterUserName).First();
- var accountWithEmail = repository.Accounts.GetByEmail(FakeDataGenerator.TwitterEmail).First();
- var originalId = accountWithUserName.Id;
+ const string editedUser = "edItedUsEr";
+ const string editedEmail = "editedEmail@twitter.example";
+ const string editedNote = "edited note text bla bla bla! ¬!\"£$%^&*()_+=-`[]#'/.,<>?|\\";
+ IAccount accountWithUserName = null;
+ var originalId = Guid.Empty;
// When
- accountWithUserName.SetField(FieldTypeKey.UserName, editedUser);
- accountWithUserName.SetField(FieldTypeKey.Email, editedEmail);
- accountWithUserName.Save();
- var retrievedAccount = repository.Accounts.GetByUserName(editedUser).First();
+ Action<IRepository> actLoadAndEditAccount = repository => {
+ accountWithUserName = repository.Accounts.GetByUserName(FakeDataGenerator.TwitterUserName).First();
+ originalId = accountWithUserName.Id;
+ accountWithUserName.SetField(FieldTypeKey.UserName, editedUser);
+ accountWithUserName.SetField(FieldTypeKey.Email, editedEmail);
+ accountWithUserName.Notes = editedNote;
+ accountWithUserName.Save();
+ };
// Then
- retrievedAccount.Id.Should().Be(originalId);
- retrievedAccount.GetDefaultField(FieldTypeKey.UserName).Value.Should().Be(editedUser);
- retrievedAccount.GetDefaultField(FieldTypeKey.Email).Value.Should().Be(editedEmail);
+ TestWithBothRepositories(repository => {
+ if (accountWithUserName == null) {
+ actLoadAndEditAccount(repository);
+ }
+ var retrievedAccount = repository.Accounts.GetByUserName(editedUser).First();
+ retrievedAccount.Id.Should().Be(originalId);
+ retrievedAccount.GetDefaultField(FieldTypeKey.UserName).Value.Should().Be(editedUser);
+ retrievedAccount.GetDefaultField(FieldTypeKey.Email).Value.Should().Be(editedEmail);
+ retrievedAccount.Notes.Should().Be(editedNote);
+ });
}
@@ -190,45 +213,87 @@ public abstract class RepositoryTests {
public void When_A_Tag_Is_Added_Then_The_Number_Of_Tags_Should_Increase() {
// Given
- var repository = GetRepositoryWithFakeData();
- var account = repository.Accounts.GetByUserName(FakeDataGenerator.TwitterUserName).First();
- var originalTotalTagCount = repository.Tags.Count();
- var originalAccountTagCount = account.Tags.Count();
+ IAccount twitterAccount = null;
+ var originalTotalTagCount = -1;
+ var originalAccountTagCount = -1;
+ const string testTagA = "test-tag-A";
+ const string testTagB = "test-tag-B";
+ const string testNonValidTag = "unvalid tag";
// When
- var actAddTag = new Action(() => account.AddTag("test tag A"));
- var actSave = new Action(account.Save);
-
+ Action<IAccount> actAddTag = account => account.Tags.Add(testTagA);
+ Action<IAccount> actSave = account => account.Save();
+ Action<IRepository> actLoadAccount = repository =>
+ twitterAccount = repository.Accounts.GetByUserName(FakeDataGenerator.TwitterUserName).First();
+
// Then
- account.IsDirty.Should().BeFalse();
- actAddTag();
- account.IsDirty.Should().BeTrue();
- repository.Tags.Count().Should().Be(originalTotalTagCount);
- actSave();
- account.IsDirty.Should().BeFalse();
- account.Tags.Count().Should().Be(originalAccountTagCount + 1);
- repository.Tags.Count().Should().Be(originalTotalTagCount + 1);
- repository.Tags.Contains("test tag b").Should().BeFalse();
- repository.Tags.Contains("test tag a").Should().BeTrue();
-
+ TestWithUnsavedFakeData(repository => {
+ actLoadAccount(repository);
+ originalTotalTagCount = repository.GetAllTags().Count();
+ originalAccountTagCount = twitterAccount.Tags.Count();
+ twitterAccount.IsDirty.Should().BeFalse();
+ actAddTag(twitterAccount);
+ twitterAccount.IsDirty.Should().BeTrue();
+ repository.GetAllTags().Count().Should().Be(originalTotalTagCount + 1);
+ repository.GetAllTags().Count().Should().BeGreaterOrEqualTo(twitterAccount.Tags.Count());
+ actSave(twitterAccount);
+ });
+ TestWithReloadedRepository(repository => {
+ actLoadAccount(repository);
+ twitterAccount.IsDirty.Should().BeFalse();
+ twitterAccount.Tags.Count().Should().Be(originalAccountTagCount + 1);
+ repository.GetAllTags().Count().Should().Be(originalTotalTagCount + 1);
+ repository.GetAllTags().Any(tag => tag.Key.Equals(testTagA, StringComparison.OrdinalIgnoreCase)).Should().BeTrue();
+ repository.GetAllTags().Any(tag => tag.Key.Equals(testTagB, StringComparison.OrdinalIgnoreCase)).Should().BeFalse();
+ repository.GetAllTags().Count().Should().BeGreaterOrEqualTo(twitterAccount.Tags.Count());
+ var accounts = repository.GetAllTags().First(tag => tag.Key.Equals(testTagA, StringComparison.OrdinalIgnoreCase)).Accounts;
+ accounts.Should().Contain(account => account.Equals(twitterAccount));
+ });
}
[Test]
public void When_A_Tag_Is_Deleted_Then_The_Number_Of_Tags_Should_Decrease() {
- throw new NotImplementedException();
- }
+ // Given
+ IAccount googleAccount = null;
+ var originalTotalTagCount = -1;
+ var originalAccountTagCount = -1;
+ // When
+ Action<IRepository> actLoadAccount = repository =>
+ googleAccount = repository.Accounts.GetByEmail(FakeDataGenerator.Google1Email).First();
+ Action actDeleteTag = () => {
+ googleAccount.Tags.Remove(FakeDataGenerator.Tag1);
+ googleAccount.Tags.Remove(FakeDataGenerator.Tag2);
+ googleAccount.Tags.Remove(FakeDataGenerator.Tag3);
+ };
+ Action actSave = () => googleAccount.Save();
- [Test]
- public void When_A_Tag_Is_Changed_Then_The_Change_Should_Be_Persisted() {
- throw new NotImplementedException();
+ // Then
+ TestWithUnsavedFakeData(repository => {
+ actLoadAccount(repository);
+ originalTotalTagCount = repository.GetAllTags().Count();
+ originalAccountTagCount = googleAccount.Tags.Count();
+ googleAccount.IsDirty.Should().BeFalse();
+ actDeleteTag();
+ googleAccount.IsDirty.Should().BeTrue();
+ googleAccount.Tags.Count().Should().Be(0);
+ repository.GetAllTags().Count().Should().BeGreaterOrEqualTo(googleAccount.Tags.Count());
+ actSave();
+ });
+ TestWithReloadedRepository(repository => {
+ actLoadAccount(repository);
+ googleAccount.IsDirty.Should().BeFalse();
+ googleAccount.Tags.Count().Should().Be(0);
+ repository.GetAllTags().Count().Should().BeGreaterOrEqualTo(0);
+ repository.GetAllTags().Any(tag => tag.Key.Equals(FakeDataGenerator.Tag1, StringComparison.OrdinalIgnoreCase)).Should().BeTrue();
+ });
}
[Test]
public void When_A_Password_Is_Changed_Then_The_Account_Should_Not_Be_Dirty_And_The_Password_Shold_Be_Saved() {
// Given
- var repository = GetRepositoryWithFakeData();
+ var repository = GetNewRepositoryWithFakeData();
var account = repository.Accounts.GetByUserName(FakeDataGenerator.TwitterUserName).First();
var originalDefaultPassword = account.GetPassword();
var originalCustomPassword = account.GetPassword("custom");
View
3  src/PassFruit.Ui.Wp/Init.cs
@@ -24,7 +24,8 @@ public class Init {
public Repositories GetRepositories() {
var repositories = new Repositories();
- var fakeRepository = new InMemoryRepository();
+ var repositoryConfiguration = new InMemoryRepositoryConfiguration("");
+ var fakeRepository = new InMemoryRepository(repositoryConfiguration);
repositories.AddRepository(fakeRepository);
repositories.SelectRepository(fakeRepository);
return repositories;
View
2  src/PassFruit.Ui.Wp/MainPageViewModel.cs
@@ -61,7 +61,7 @@ public class MainPageViewModel : Screen {
private void PopulateTags() {
Tags.Clear();
- var tagViewModels = _repository.Tags.Select(accountTag => new TagViewModel(accountTag));
+ var tagViewModels = _repository.GetAllTags().Select(accountTag => new TagViewModel(accountTag));
foreach (var tagViewModel in tagViewModels) {
Tags.Add(tagViewModel);
}
View
2  src/PassFruit.Ui.Wp/Views/TagsPivotPageViewModel.cs
@@ -48,7 +48,7 @@ public class TagsPivotPageViewModel : Conductor<TagViewModel>.Collection.OneActi
private void PopulateTags() {
var repository = Init.GetRepository();
_tags = new ObservableCollection<TagViewModel>();
- var tagViewModels = repository.Tags.Select(accountTag => new TagViewModel(accountTag));
+ var tagViewModels = repository.GetAllTags().Select(accountTag => new TagViewModel(accountTag));
foreach (var tagViewModel in tagViewModels) {
Tags.Add(tagViewModel);
}
View
31 src/PassFruit/Account.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using PassFruit.Contracts;
using PassFruit.FieldImpl;
@@ -12,16 +13,15 @@ public class Account : IAccount {
private int _orignalHash;
- private readonly List<ITag> _tags;
-
private readonly List<IField> _fields;
internal Account(IRepository repository, IProvider provider, Guid? id = null) {
_repository = repository;
_provider = provider;
- _tags = new List<ITag>();
- _fields = new List<IField>();
Id = id.HasValue ? id.Value : Guid.NewGuid();
+ _fields = new List<IField>();
+ Tags = new Tags(_repository);
+ Notes = "";
}
public Guid Id { get; private set; }
@@ -73,7 +73,7 @@ public class Account : IAccount {
_repository.SetPassword(Id, password, passwordKey);
}
- public IEnumerable<ITag> Tags { get { return _tags.ToArray(); } }
+ public ITags Tags { get; private set; }
public IEnumerable<IField> Fields { get { return _fields.ToArray(); } }
@@ -101,8 +101,16 @@ public class Account : IAccount {
}
}
- public void AddTag(string tagName) {
- _tags.Add(_repository.Tags.Create(tagName));
+ public void AddTag(string tagKey) {
+ Tags.Add(tagKey);
+ }
+
+ public void DeleteTag(string tagKey) {
+ Tags.Remove(tagKey);
+ }
+
+ public void DeleteAllPasswords() {
+ _repository.DeletePasswords(Id);
}
public virtual void Save() {
@@ -122,13 +130,20 @@ public class Account : IAccount {
public override int GetHashCode() {
unchecked {
var result = Id.GetHashCode();
+ Debug.WriteLine("Account ID: " + Id);
+
result = (result * 397) ^ (Notes != null ? Notes.GetHashCode() : 0);
+ Debug.WriteLine(" - HashCode after notes: " + result);
foreach (var field in Fields) {
result = (result * 397) ^ (field != null ? field.GetHashCode() : 0);
}
+ Debug.WriteLine(" - Fields: " + string.Join(", ", Fields.Select(field => field.Name + "<" + field.FieldType.Key + "> " + field.Value).ToArray()));
+ Debug.WriteLine(" - HashCode after fields: " + result);
foreach (var tag in Tags) {
result = (result * 397) ^ (tag != null ? tag.GetHashCode() : 0);
}
+ Debug.WriteLine(" - Tags: " + string.Join(", ", Tags.Select(tag => tag.Key).ToArray()));
+ Debug.WriteLine(" - HashCode after tags: " + result);
return result;
}
}
@@ -136,7 +151,7 @@ public class Account : IAccount {
public bool Equals(IAccount other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
- return other.Id.Equals(Id) && Equals(other.Notes, Notes) && Equals(other.Tags, Tags);
+ return other.GetHashCode() == GetHashCode();
}
public override bool Equals(object obj) {
View
1  src/PassFruit/Accounts.cs
@@ -44,6 +44,7 @@ public class Accounts : Collection<IAccount>, IAccounts {
base.RemoveItem(index);
}
} else {
+ this[index].DeleteAllPasswords();
this[index] = new DeletedAccount(_repository, accountId);
}
}
View
2  src/PassFruit/Field.cs
@@ -33,7 +33,7 @@ public class Field : IField {
public override int GetHashCode() {
unchecked {
- int result = (FieldType != null ? FieldType.GetHashCode() : 0);
+ int result = (FieldType != null ? FieldType.Key.GetHashCode() : 0);
result = (result * 397) ^ (Value != null ? Value.GetHashCode() : 0);
result = (result * 397) ^ (Name != null ? Name.GetHashCode() : 0);
return result;
View
23 src/PassFruit/RepositoryBase.cs
@@ -14,12 +14,10 @@ public abstract class RepositoryBase : IRepository {
Configuration = configuration;
}
- protected IRepositoryConfiguration Configuration { get; set; }
+ public IRepositoryConfiguration Configuration { get; private set; }
private IAccounts _accounts;
- private ITags _tags;
-
private IProviders _providers;
private IFieldTypes _fieldTypes;
@@ -32,7 +30,7 @@ public abstract class RepositoryBase : IRepository {
get {
if (_accounts == null) {
_accounts = new Accounts(this);
- foreach (var account in GetAllAccountIds().Select(GetAccount).Where(account => account != null)) {
+ foreach (var account in GetAllAccountIds().Select(LoadAccount).Where(account => account != null)) {
_accounts.Add(account);
}
}
@@ -40,8 +38,8 @@ public abstract class RepositoryBase : IRepository {
}
}
- public ITags Tags {
- get { return _tags ?? (_tags = new Tags(this)); }
+ public IEnumerable<ITag> GetAllTags() {
+ return Accounts.SelectMany(account => account.Tags).Distinct();
}
public IProviders Providers {
@@ -69,7 +67,6 @@ public abstract class RepositoryBase : IRepository {
public abstract void SetPassword(Guid accountId, string password, string passwordKey = DefaultPasswordKey);
public void Save(IAccount account) {
- _tags = null;
if (OnSaving != null) {
OnSaving(this, new RepositorySaveEventArgs(this, account));
}
@@ -84,6 +81,12 @@ public abstract class RepositoryBase : IRepository {
return Accounts.Any(account => account.IsDirty);
}
+ public abstract void DeletePasswords(Guid accountId);
+
+ public IEnumerable<IAccount> GetDeletedAccounts() {
+ return GetDeletedAccountIds().Select(LoadAccount).Where(account => account != null).ToList();
+ }
+
protected abstract void InternalSave(IAccount account);
public event EventHandler<RepositorySaveEventArgs> OnSaved;
@@ -104,9 +107,11 @@ public abstract class RepositoryBase : IRepository {
}
}
- protected abstract IEnumerable<Guid> GetAllAccountIds(bool includingDeleted = false);
+ protected abstract IEnumerable<Guid> GetAllAccountIds();
+
+ protected abstract IEnumerable<Guid> GetDeletedAccountIds();
- protected abstract IAccount GetAccount(Guid accountId);
+ protected abstract IAccount LoadAccount(Guid accountId);
protected void LoadAllAccountProviders() {
Providers.Add("generic", "Generic", true, true, false, "");
View
12 src/PassFruit/Tag.cs
@@ -10,11 +10,19 @@ public class Tag : ITag {
private readonly IRepository _repository;
- internal Tag(IRepository repository) {
+ internal Tag(IRepository repository, string key) {
_repository = repository;
+ CheckForValidity(key);
+ Key = key.ToLowerInvariant();
}
- public string Key { get; set; }
+ private void CheckForValidity(string key) {
+ if (key.Contains(" ")) {
+ throw new Exception("Space character is not valid in a tag. Use a dash (-) instead.");
+ }
+ }
+
+ public string Key { get; private set; }
public IEnumerable<IAccount> Accounts {
get { return _repository.Accounts.Where(account => account.Tags.Contains(this)); }
View
37 src/PassFruit/Tags.cs
@@ -14,12 +14,6 @@ public class Tags : ITags {
internal Tags(IRepository repository) {
_repository = repository;
- ReloadTags();
- }
-
- private void ReloadTags() {
- _tags.Clear();
- _tags.AddRange(_repository.Accounts.SelectMany(account => account.Tags).Distinct());
}
public ITag this[string key] {
@@ -33,8 +27,18 @@ public class Tags : ITags {
return _tags.Any(accountTag => accountTag.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
}
- public IEnumerable<ITag> GetByAccountId(Guid accountId) {
- return this.Where(accountTag => accountTag.Accounts.Any(account => account.Id == accountId));
+ public void Add(string key) {
+ if (Contains(key)) {
+ throw new Exception(string.Format("The tag '{0}' has already been added to the list", key));
+ }
+ _tags.Add(new Tag(_repository, key));
+ }
+
+ public void Remove(string key) {
+ if (!Contains(key)) {
+ throw new Exception(string.Format("The tag '{0}' has not been found in the list", key));
+ }
+ _tags.Remove(this[key]);
}
public IEnumerator<ITag> GetEnumerator() {
@@ -45,8 +49,21 @@ public class Tags : ITags {
return GetEnumerator();
}
- public ITag Create(string key) {
- return new Tag(_repository) { Key = key };
+ public bool Equals(Tags other) {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return Equals(other._tags, _tags);
+ }
+
+ public override bool Equals(object obj) {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ if (obj.GetType() != typeof (Tags)) return false;
+ return Equals((Tags) obj);
+ }
+
+ public override int GetHashCode() {
+ return _tags.GetHashCode();
}
}

No commit comments for this range

Something went wrong with that request. Please try again.