Complete: Accounts, persistence, unit tests
This commit is contained in:
parent
643ae09b2b
commit
1ad2135a3f
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using AudibleApi;
|
using AudibleApi;
|
||||||
using AudibleApi.Authorization;
|
using AudibleApi.Authorization;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
@ -28,7 +29,12 @@ namespace InternalUtilities
|
|||||||
{
|
{
|
||||||
public event EventHandler Updated;
|
public event EventHandler Updated;
|
||||||
private void update(object sender = null, EventArgs e = null)
|
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() { }
|
public Accounts() { }
|
||||||
|
|
||||||
@ -45,15 +51,13 @@ namespace InternalUtilities
|
|||||||
// 'set' is only used by json deser
|
// 'set' is only used by json deser
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_accountsSettings_backing = value;
|
if (value is null)
|
||||||
|
|
||||||
if (_accountsSettings_backing is null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var acct in _accountsSettings_backing)
|
foreach (var account in value)
|
||||||
acct.Updated += update;
|
_add(account);
|
||||||
|
|
||||||
update();
|
update_no_validate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
@ -66,29 +70,78 @@ namespace InternalUtilities
|
|||||||
public string ToJson(Formatting formatting = Formatting.Indented)
|
public string ToJson(Formatting formatting = Formatting.Indented)
|
||||||
=> JsonConvert.SerializeObject(this, formatting, Identity.GetJsonSerializerSettings());
|
=> JsonConvert.SerializeObject(this, formatting, Identity.GetJsonSerializerSettings());
|
||||||
|
|
||||||
public void UNITTEST_Seed(Account account)
|
public void Add(Account account)
|
||||||
{
|
{
|
||||||
_accountsSettings_backing.Add(account);
|
_add(account);
|
||||||
update();
|
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
|
_accountsSettings_backing.Add(account);
|
||||||
// removing: unsubscribe
|
account.Updated += update;
|
||||||
|
}
|
||||||
|
|
||||||
// IEnumerable<Account> GetAllAccounts
|
// more common naming convention alias for internal collection
|
||||||
|
public IReadOnlyList<Account> GetAll() => AccountsSettings;
|
||||||
|
|
||||||
// void UpsertAccount (id, locale)
|
public Account GetAccount(string accountId, string locale)
|
||||||
// if not exists
|
{
|
||||||
// create account w/null identity
|
if (locale is null)
|
||||||
// save in file
|
return null;
|
||||||
// return Account?
|
|
||||||
// return bool/enum of whether is newly created?
|
|
||||||
|
|
||||||
// how to persist edits to [Account] obj?
|
return AccountsSettings.SingleOrDefault(a => a.AccountId == accountId && a.IdentityTokens.Locale.Name == locale);
|
||||||
// account name, decryptkey, id tokens, ...
|
}
|
||||||
// persistence happens in [Accounts], not [Account]. an [Account] accidentally created directly shouldn't mess up expected workflow ???
|
|
||||||
|
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
|
public class Account : Updatable
|
||||||
{
|
{
|
||||||
|
|||||||
@ -10,6 +10,7 @@ using AudibleApi;
|
|||||||
using AudibleApi.Authorization;
|
using AudibleApi.Authorization;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using FluentAssertions.Common;
|
||||||
using InternalUtilities;
|
using InternalUtilities;
|
||||||
using Microsoft.VisualStudio.TestPlatform.Common.Filtering;
|
using Microsoft.VisualStudio.TestPlatform.Common.Filtering;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
@ -133,7 +134,7 @@ namespace AccountsTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AccountsPersisterTestBase
|
public class AccountsTestBase
|
||||||
{
|
{
|
||||||
protected string TestFile;
|
protected string TestFile;
|
||||||
|
|
||||||
@ -153,7 +154,7 @@ namespace AccountsTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class ctor : AccountsPersisterTestBase
|
public class ctor : AccountsTestBase
|
||||||
{
|
{
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void create_file()
|
public void create_file()
|
||||||
@ -190,8 +191,8 @@ namespace AccountsTests
|
|||||||
public void save_multiple_children()
|
public void save_multiple_children()
|
||||||
{
|
{
|
||||||
var accounts = new Accounts();
|
var accounts = new Accounts();
|
||||||
accounts.UNITTEST_Seed(new Account("a0") { AccountName = "n0" });
|
accounts.Add(new Account("a0") { AccountName = "n0" });
|
||||||
accounts.UNITTEST_Seed(new Account("a1") { AccountName = "n1" });
|
accounts.Add(new Account("a1") { AccountName = "n1" });
|
||||||
|
|
||||||
// dispose to cease auto-updates
|
// dispose to cease auto-updates
|
||||||
using (var p = new AccountsPersister(accounts, TestFile)) { }
|
using (var p = new AccountsPersister(accounts, TestFile)) { }
|
||||||
@ -209,7 +210,7 @@ namespace AccountsTests
|
|||||||
var idJson = JsonConvert.SerializeObject(id, Identity.GetJsonSerializerSettings());
|
var idJson = JsonConvert.SerializeObject(id, Identity.GetJsonSerializerSettings());
|
||||||
|
|
||||||
var accounts = new Accounts();
|
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
|
// dispose to cease auto-updates
|
||||||
using (var p = new AccountsPersister(accounts, TestFile)) { }
|
using (var p = new AccountsPersister(accounts, TestFile)) { }
|
||||||
@ -222,7 +223,7 @@ namespace AccountsTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class save : AccountsPersisterTestBase
|
public class save : AccountsTestBase
|
||||||
{
|
{
|
||||||
// add/save account after file creation
|
// add/save account after file creation
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
@ -238,7 +239,7 @@ namespace AccountsTests
|
|||||||
var idIn = new Identity(localeIn);
|
var idIn = new Identity(localeIn);
|
||||||
var acctIn = new Account("a0") { AccountName = "n0", IdentityTokens = idIn };
|
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
|
// re-load file. ensure account still exists
|
||||||
@ -266,7 +267,7 @@ namespace AccountsTests
|
|||||||
var idIn = new Identity(localeIn);
|
var idIn = new Identity(localeIn);
|
||||||
var acctIn = new Account("a0") { AccountName = "n0", IdentityTokens = idIn };
|
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
|
// re-load file. ensure account still exists
|
||||||
@ -286,7 +287,7 @@ namespace AccountsTests
|
|||||||
var idIn = new Identity(localeIn);
|
var idIn = new Identity(localeIn);
|
||||||
var acctIn = new Account("a1") { AccountName = "n1", IdentityTokens = idIn };
|
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
|
// 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
|
// update Account property. must be non-destructive to all other data
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void update_Account_field()
|
public void update_Account_field()
|
||||||
@ -317,13 +344,13 @@ namespace AccountsTests
|
|||||||
var locale1 = Localization.Locales.Single(l => l.Name == "us");
|
var locale1 = Localization.Locales.Single(l => l.Name == "us");
|
||||||
var id1 = new Identity(locale1);
|
var id1 = new Identity(locale1);
|
||||||
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
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 locale2 = Localization.Locales.Single(l => l.Name == "uk");
|
||||||
var id2 = new Identity(locale2);
|
var id2 = new Identity(locale2);
|
||||||
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
||||||
|
|
||||||
p.Accounts.UNITTEST_Seed(acct2);
|
p.Accounts.Add(acct2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update AccountName on existing file
|
// update AccountName on existing file
|
||||||
@ -363,13 +390,13 @@ namespace AccountsTests
|
|||||||
var locale1 = Localization.Locales.Single(l => l.Name == "us");
|
var locale1 = Localization.Locales.Single(l => l.Name == "us");
|
||||||
var id1 = new Identity(locale1);
|
var id1 = new Identity(locale1);
|
||||||
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
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 locale2 = Localization.Locales.Single(l => l.Name == "uk");
|
||||||
var id2 = new Identity(locale2);
|
var id2 = new Identity(locale2);
|
||||||
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
||||||
|
|
||||||
p.Accounts.UNITTEST_Seed(acct2);
|
p.Accounts.Add(acct2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update identity on existing file
|
// update identity on existing file
|
||||||
@ -413,13 +440,13 @@ namespace AccountsTests
|
|||||||
var locale1 = Localization.Locales.Single(l => l.Name == "us");
|
var locale1 = Localization.Locales.Single(l => l.Name == "us");
|
||||||
var id1 = new Identity(locale1);
|
var id1 = new Identity(locale1);
|
||||||
var acct1 = new Account("a0") { AccountName = "n0", IdentityTokens = id1 };
|
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 locale2 = Localization.Locales.Single(l => l.Name == "uk");
|
||||||
var id2 = new Identity(locale2);
|
var id2 = new Identity(locale2);
|
||||||
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
var acct2 = new Account("a1") { AccountName = "n1", IdentityTokens = id2 };
|
||||||
|
|
||||||
p.Accounts.UNITTEST_Seed(acct2);
|
p.Accounts.Add(acct2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update identity on existing file
|
// 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