Complete: Accounts, persistence, unit tests
This commit is contained in:
parent
643ae09b2b
commit
1ad2135a3f
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AudibleApi;
|
||||
using AudibleApi.Authorization;
|
||||
using Dinah.Core;
|
||||
@ -28,7 +29,12 @@ namespace InternalUtilities
|
||||
{
|
||||
public event EventHandler Updated;
|
||||
private void update(object sender = null, EventArgs e = null)
|
||||
=> Updated?.Invoke(this, new EventArgs());
|
||||
{
|
||||
foreach (var account in AccountsSettings)
|
||||
validate(account);
|
||||
update_no_validate();
|
||||
}
|
||||
private void update_no_validate() => Updated?.Invoke(this, new EventArgs());
|
||||
|
||||
public Accounts() { }
|
||||
|
||||
@ -45,15 +51,13 @@ namespace InternalUtilities
|
||||
// 'set' is only used by json deser
|
||||
set
|
||||
{
|
||||
_accountsSettings_backing = value;
|
||||
|
||||
if (_accountsSettings_backing is null)
|
||||
if (value is null)
|
||||
return;
|
||||
|
||||
foreach (var acct in _accountsSettings_backing)
|
||||
acct.Updated += update;
|
||||
foreach (var account in value)
|
||||
_add(account);
|
||||
|
||||
update();
|
||||
update_no_validate();
|
||||
}
|
||||
}
|
||||
[JsonIgnore]
|
||||
@ -66,29 +70,78 @@ namespace InternalUtilities
|
||||
public string ToJson(Formatting formatting = Formatting.Indented)
|
||||
=> JsonConvert.SerializeObject(this, formatting, Identity.GetJsonSerializerSettings());
|
||||
|
||||
public void UNITTEST_Seed(Account account)
|
||||
public void Add(Account account)
|
||||
{
|
||||
_accountsSettings_backing.Add(account);
|
||||
update();
|
||||
_add(account);
|
||||
update_no_validate();
|
||||
}
|
||||
|
||||
// replace UNITTEST_Seed
|
||||
public void _add(Account account)
|
||||
{
|
||||
validate(account);
|
||||
|
||||
// when creating Account object (get, update, insert), subscribe to it's update. including all new ones on initial load
|
||||
// removing: unsubscribe
|
||||
_accountsSettings_backing.Add(account);
|
||||
account.Updated += update;
|
||||
}
|
||||
|
||||
// IEnumerable<Account> GetAllAccounts
|
||||
// more common naming convention alias for internal collection
|
||||
public IReadOnlyList<Account> GetAll() => AccountsSettings;
|
||||
|
||||
// void UpsertAccount (id, locale)
|
||||
// if not exists
|
||||
// create account w/null identity
|
||||
// save in file
|
||||
// return Account?
|
||||
// return bool/enum of whether is newly created?
|
||||
public Account GetAccount(string accountId, string locale)
|
||||
{
|
||||
if (locale is null)
|
||||
return null;
|
||||
|
||||
// how to persist edits to [Account] obj?
|
||||
// account name, decryptkey, id tokens, ...
|
||||
// persistence happens in [Accounts], not [Account]. an [Account] accidentally created directly shouldn't mess up expected workflow ???
|
||||
return AccountsSettings.SingleOrDefault(a => a.AccountId == accountId && a.IdentityTokens.Locale.Name == locale);
|
||||
}
|
||||
|
||||
public Account Upsert(string accountId, string locale)
|
||||
{
|
||||
var acct = GetAccount(accountId, locale);
|
||||
|
||||
if (acct != null)
|
||||
return acct;
|
||||
|
||||
var l = Localization.Locales.Single(l => l.Name == locale);
|
||||
var id = new Identity(l);
|
||||
|
||||
var account = new Account(accountId) { IdentityTokens = id };
|
||||
Add(account);
|
||||
return account;
|
||||
}
|
||||
|
||||
public bool Delete(Account account)
|
||||
{
|
||||
if (!_accountsSettings_backing.Contains(account))
|
||||
return false;
|
||||
|
||||
account.Updated -= update;
|
||||
return _accountsSettings_backing.Remove(account);
|
||||
}
|
||||
|
||||
public bool Delete(string accountId, string locale)
|
||||
{
|
||||
var acct = GetAccount(accountId, locale);
|
||||
if (acct is null)
|
||||
return false;
|
||||
return Delete(acct);
|
||||
}
|
||||
|
||||
private void validate(Account account)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNull(account, nameof(account));
|
||||
|
||||
var accountId = account.AccountId;
|
||||
var locale = account?.IdentityTokens?.Locale?.Name;
|
||||
|
||||
var acct = GetAccount(accountId, locale);
|
||||
|
||||
if (acct is null || account is null)
|
||||
return;
|
||||
|
||||
if (acct != account)
|
||||
throw new InvalidOperationException("Cannot add an account with the same account Id and Locale");
|
||||
}
|
||||
}
|
||||
public class Account : Updatable
|
||||
{
|
||||
|
||||
@ -10,6 +10,7 @@ using AudibleApi;
|
||||
using AudibleApi.Authorization;
|
||||
using Dinah.Core;
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Common;
|
||||
using InternalUtilities;
|
||||
using Microsoft.VisualStudio.TestPlatform.Common.Filtering;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
@ -133,7 +134,7 @@ namespace AccountsTests
|
||||
}
|
||||
}
|
||||
|
||||
public class AccountsPersisterTestBase
|
||||
public class AccountsTestBase
|
||||
{
|
||||
protected string TestFile;
|
||||
|
||||
@ -153,7 +154,7 @@ namespace AccountsTests
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class ctor : AccountsPersisterTestBase
|
||||
public class ctor : AccountsTestBase
|
||||
{
|
||||
[TestMethod]
|
||||
public void create_file()
|
||||
@ -190,8 +191,8 @@ namespace AccountsTests
|
||||
public void save_multiple_children()
|
||||
{
|
||||
var accounts = new Accounts();
|
||||
accounts.UNITTEST_Seed(new Account("a0") { AccountName = "n0" });
|
||||
accounts.UNITTEST_Seed(new Account("a1") { AccountName = "n1" });
|
||||
accounts.Add(new Account("a0") { AccountName = "n0" });
|
||||
accounts.Add(new Account("a1") { AccountName = "n1" });
|
||||
|
||||
// dispose to cease auto-updates
|
||||
using (var p = new AccountsPersister(accounts, TestFile)) { }
|
||||
@ -209,7 +210,7 @@ namespace AccountsTests
|
||||
var idJson = JsonConvert.SerializeObject(id, Identity.GetJsonSerializerSettings());
|
||||
|
||||
var accounts = new Accounts();
|
||||
accounts.UNITTEST_Seed(new Account("a0") { AccountName = "n0", IdentityTokens = id });
|
||||
accounts.Add(new Account("a0") { AccountName = "n0", IdentityTokens = id });
|
||||
|
||||
// dispose to cease auto-updates
|
||||
using (var p = new AccountsPersister(accounts, TestFile)) { }
|
||||
@ -222,7 +223,7 @@ namespace AccountsTests
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class save : AccountsPersisterTestBase
|
||||
public class save : AccountsTestBase
|
||||
{
|
||||
// add/save account after file creation
|
||||
[TestMethod]
|
||||
@ -238,7 +239,7 @@ namespace AccountsTests
|
||||
var idIn = new Identity(localeIn);
|
||||
var acctIn = new Account("a0") { AccountName = "n0", IdentityTokens = idIn };
|
||||
|
||||
p.Accounts.UNITTEST_Seed(acctIn);
|
||||
p.Accounts.Add(acctIn);
|
||||
}
|
||||
|
||||
// re-load file. ensure account still exists
|
||||
@ -266,7 +267,7 @@ namespace AccountsTests
|
||||
var idIn = new Identity(localeIn);
|
||||
var acctIn = new Account("a0") { AccountName = "n0", IdentityTokens = idIn };
|
||||
|
||||
p.Accounts.UNITTEST_Seed(acctIn);
|
||||
p.Accounts.Add(acctIn);
|
||||
}
|
||||
|
||||
// re-load file. ensure account still exists
|
||||
@ -286,7 +287,7 @@ namespace AccountsTests
|
||||
var idIn = new Identity(localeIn);
|
||||
var acctIn = new Account("a1") { AccountName = "n1", IdentityTokens = idIn };
|
||||
|
||||
p.Accounts.UNITTEST_Seed(acctIn);
|
||||
p.Accounts.Add(acctIn);
|
||||
}
|
||||
|
||||
// re-load file. ensure both accounts still exist
|
||||
@ -304,6 +305,32 @@ namespace AccountsTests
|
||||
}
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void update_Account_field_just_added()
|
||||
{
|
||||
// create initial file
|
||||
using (var p = new AccountsPersister(new Accounts(), TestFile)) { }
|
||||
|
||||
// load file. create 2 accounts
|
||||
using (var p = new AccountsPersister(TestFile))
|
||||
{
|
||||
var locale1 = Localization.Locales.Single(l => l.Name == "us");
|
||||
var id1 = new Identity(locale1);
|
||||
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
||||
p.Accounts.Add(acct1);
|
||||
|
||||
// update just-added item. note: this is different than the subscription which happens on initial collection load. ensure this works also
|
||||
acct1.AccountName = "new";
|
||||
}
|
||||
|
||||
// verify save property
|
||||
using (var p = new AccountsPersister(TestFile))
|
||||
{
|
||||
var acct0 = p.Accounts.AccountsSettings[0];
|
||||
acct0.AccountName.Should().Be("new");
|
||||
}
|
||||
}
|
||||
|
||||
// update Account property. must be non-destructive to all other data
|
||||
[TestMethod]
|
||||
public void update_Account_field()
|
||||
@ -317,13 +344,13 @@ namespace AccountsTests
|
||||
var locale1 = Localization.Locales.Single(l => l.Name == "us");
|
||||
var id1 = new Identity(locale1);
|
||||
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
||||
p.Accounts.UNITTEST_Seed(acct1);
|
||||
p.Accounts.Add(acct1);
|
||||
|
||||
var locale2 = Localization.Locales.Single(l => l.Name == "uk");
|
||||
var id2 = new Identity(locale2);
|
||||
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
||||
|
||||
p.Accounts.UNITTEST_Seed(acct2);
|
||||
p.Accounts.Add(acct2);
|
||||
}
|
||||
|
||||
// update AccountName on existing file
|
||||
@ -363,13 +390,13 @@ namespace AccountsTests
|
||||
var locale1 = Localization.Locales.Single(l => l.Name == "us");
|
||||
var id1 = new Identity(locale1);
|
||||
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
||||
p.Accounts.UNITTEST_Seed(acct1);
|
||||
p.Accounts.Add(acct1);
|
||||
|
||||
var locale2 = Localization.Locales.Single(l => l.Name == "uk");
|
||||
var id2 = new Identity(locale2);
|
||||
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
||||
|
||||
p.Accounts.UNITTEST_Seed(acct2);
|
||||
p.Accounts.Add(acct2);
|
||||
}
|
||||
|
||||
// update identity on existing file
|
||||
@ -413,13 +440,13 @@ namespace AccountsTests
|
||||
var locale1 = Localization.Locales.Single(l => l.Name == "us");
|
||||
var id1 = new Identity(locale1);
|
||||
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
||||
p.Accounts.UNITTEST_Seed(acct1);
|
||||
p.Accounts.Add(acct1);
|
||||
|
||||
var locale2 = Localization.Locales.Single(l => l.Name == "uk");
|
||||
var id2 = new Identity(locale2);
|
||||
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
||||
|
||||
p.Accounts.UNITTEST_Seed(acct2);
|
||||
p.Accounts.Add(acct2);
|
||||
}
|
||||
|
||||
// update identity on existing file
|
||||
@ -448,4 +475,154 @@ namespace AccountsTests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class retrieve : AccountsTestBase
|
||||
{
|
||||
[TestMethod]
|
||||
public void get_where()
|
||||
{
|
||||
var us = Localization.Locales.Single(l => l.Name == "us");
|
||||
var idUs = new Identity(us);
|
||||
var acct1 = new Account("cng") { IdentityTokens = idUs, AccountName = "foo" };
|
||||
|
||||
var uk = Localization.Locales.Single(l => l.Name == "uk");
|
||||
var idUk = new Identity(uk);
|
||||
var acct2 = new Account("cng") { IdentityTokens = idUk, AccountName = "bar" };
|
||||
|
||||
var accounts = new Accounts();
|
||||
accounts.Add(acct1);
|
||||
accounts.Add(acct2);
|
||||
|
||||
accounts.GetAccount("cng", "uk").AccountName.Should().Be("bar");
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class upsert : AccountsTestBase
|
||||
{
|
||||
[TestMethod]
|
||||
public void upsert_new()
|
||||
{
|
||||
var accounts = new Accounts();
|
||||
accounts.AccountsSettings.Count.Should().Be(0);
|
||||
|
||||
accounts.Upsert("cng", "us");
|
||||
|
||||
accounts.AccountsSettings.Count.Should().Be(1);
|
||||
accounts.GetAccount("cng", "us").AccountId.Should().Be("cng");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void upsert_exists()
|
||||
{
|
||||
var accounts = new Accounts();
|
||||
var orig = accounts.Upsert("cng", "us");
|
||||
orig.AccountName = "foo";
|
||||
|
||||
var exists = accounts.Upsert("cng", "us");
|
||||
exists.AccountName.Should().Be("foo");
|
||||
|
||||
orig.Should().IsSameOrEqualTo(exists);
|
||||
}
|
||||
}
|
||||
|
||||
[TestClass]
|
||||
public class delete : AccountsTestBase
|
||||
{
|
||||
[TestMethod]
|
||||
public void delete_account()
|
||||
{
|
||||
var accounts = new Accounts();
|
||||
var acct = accounts.Upsert("cng", "us");
|
||||
accounts.AccountsSettings.Count.Should().Be(1);
|
||||
|
||||
var removed = accounts.Delete(acct);
|
||||
removed.Should().BeTrue();
|
||||
|
||||
accounts.AccountsSettings.Count.Should().Be(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void delete_where()
|
||||
{
|
||||
var accounts = new Accounts();
|
||||
var acct = accounts.Upsert("cng", "us");
|
||||
accounts.AccountsSettings.Count.Should().Be(1);
|
||||
|
||||
accounts.Delete("baz", "baz").Should().BeFalse();
|
||||
accounts.AccountsSettings.Count.Should().Be(1);
|
||||
|
||||
accounts.Delete("cng", "us").Should().BeTrue();
|
||||
accounts.AccountsSettings.Count.Should().Be(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void deleted_account_should_not_persist_file()
|
||||
{
|
||||
Account acct;
|
||||
|
||||
using (var p = new AccountsPersister(new Accounts(), TestFile))
|
||||
{
|
||||
acct = p.Accounts.Upsert("foo", "us");
|
||||
p.Accounts.AccountsSettings.Count.Should().Be(1);
|
||||
acct.AccountName = "old";
|
||||
}
|
||||
|
||||
using (var p = new AccountsPersister(new Accounts(), TestFile))
|
||||
{
|
||||
p.Accounts.Delete(acct);
|
||||
p.Accounts.AccountsSettings.Count.Should().Be(0);
|
||||
}
|
||||
|
||||
using (var p = new AccountsPersister(new Accounts(), TestFile))
|
||||
{
|
||||
File.ReadAllText(TestFile).Should().Be("{\r\n \"AccountsSettings\": []\r\n}".Trim());
|
||||
|
||||
acct.AccountName = "new";
|
||||
|
||||
File.ReadAllText(TestFile).Should().Be("{\r\n \"AccountsSettings\": []\r\n}".Trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// account.Id + Locale.Name -- must be unique
|
||||
[TestClass]
|
||||
public class validate : AccountsTestBase
|
||||
{
|
||||
[TestMethod]
|
||||
public void violate_validation()
|
||||
{
|
||||
var accounts = new Accounts();
|
||||
|
||||
var localeIn = Localization.Locales.Single(l => l.Name == "us");
|
||||
var idIn = new Identity(localeIn);
|
||||
|
||||
var a1 = new Account("a") { AccountName = "one", IdentityTokens = idIn };
|
||||
accounts.Add(a1);
|
||||
|
||||
var a2 = new Account("a") { AccountName = "two", IdentityTokens = idIn };
|
||||
|
||||
// violation: validate()
|
||||
Assert.ThrowsException<InvalidOperationException>(() => accounts.Add(a2));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void identity_violate_validation()
|
||||
{
|
||||
var accounts = new Accounts();
|
||||
|
||||
var localeIn = Localization.Locales.Single(l => l.Name == "us");
|
||||
var idIn = new Identity(localeIn);
|
||||
|
||||
var a1 = new Account("a") { AccountName = "one", IdentityTokens = idIn };
|
||||
accounts.Add(a1);
|
||||
|
||||
var a2 = new Account("a") { AccountName = "two" };
|
||||
accounts.Add(a2);
|
||||
|
||||
// violation: GetAccount.SingleOrDefault
|
||||
Assert.ThrowsException<InvalidOperationException>(() => a2.IdentityTokens = idIn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user