2020-08-16 23:04:23 -04:00

629 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
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;
using Moq;
using Moq.Protected;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using TestAudibleApiCommon;
using TestCommon;
using static AuthorizationShared.Shared;
using static AuthorizationShared.Shared.AccessTokenTemporality;
using static TestAudibleApiCommon.ComputedTestValues;
namespace AccountsTests
{
[TestClass]
public class FromJson
{
[TestMethod]
public void _0_accounts()
{
var json = @"
{
""AccountsSettings"": []
}
".Trim();
var accounts = Accounts.FromJson(json);
accounts.AccountsSettings.Count.Should().Be(0);
}
[TestMethod]
public void _1_account_new()
{
var json = @"
{
""AccountsSettings"": [
{
""AccountId"": ""cng"",
""AccountName"": ""my main login"",
""DecryptKey"": ""asdfasdf"",
""IdentityTokens"": null
}
]
}
".Trim();
var accounts = Accounts.FromJson(json);
accounts.AccountsSettings.Count.Should().Be(1);
accounts.AccountsSettings[0].AccountId.Should().Be("cng");
accounts.AccountsSettings[0].IdentityTokens.Should().BeNull();
}
[TestMethod]
public void _1_account_populated()
{
var id = GetIdentityJson(Future);
var json = $@"
{{
""AccountsSettings"": [
{{
""AccountId"": ""cng"",
""AccountName"": ""my main login"",
""DecryptKey"": ""asdfasdf"",
""IdentityTokens"": {id}
}}
]
}}
".Trim();
var accounts = Accounts.FromJson(json);
accounts.AccountsSettings.Count.Should().Be(1);
accounts.AccountsSettings[0].AccountId.Should().Be("cng");
accounts.AccountsSettings[0].IdentityTokens.Should().NotBeNull();
accounts.AccountsSettings[0].IdentityTokens.ExistingAccessToken.TokenValue.Should().Be(AccessTokenValue);
}
}
[TestClass]
public class ToJson
{
[TestMethod]
public void serialize()
{
var id = JsonConvert.SerializeObject(Identity.Empty, Identity.GetJsonSerializerSettings());
var jsonIn = $@"
{{
""AccountsSettings"": [
{{
""AccountId"": ""cng"",
""AccountName"": ""my main login"",
""DecryptKey"": ""asdfasdf"",
""IdentityTokens"": {id}
}}
]
}}
".Trim();
var accounts = Accounts.FromJson(jsonIn);
var jsonOut = accounts.ToJson();
jsonOut.Should().Be(@"
{
""AccountsSettings"": [
{
""AccountId"": ""cng"",
""AccountName"": ""my main login"",
""DecryptKey"": ""asdfasdf"",
""IdentityTokens"": {
""LocaleName"": ""[empty]"",
""ExistingAccessToken"": {
""TokenValue"": ""Atna|"",
""Expires"": ""9999-12-31T23:59:59.9999999""
},
""PrivateKey"": null,
""AdpToken"": null,
""RefreshToken"": null,
""Cookies"": []
}
}
]
}
".Trim());
}
}
public class AccountsTestBase
{
protected string TestFile;
protected void WriteToTestFile(string contents)
=> File.WriteAllText(TestFile, contents);
[TestInitialize]
public void TestInit()
=> TestFile = Guid.NewGuid() + ".txt";
[TestCleanup]
public void TestCleanup()
{
if (File.Exists(TestFile))
File.Delete(TestFile);
}
}
[TestClass]
public class ctor : AccountsTestBase
{
[TestMethod]
public void create_file()
{
File.Exists(TestFile).Should().BeFalse();
var accounts = new Accounts();
_ = new AccountsPersister(accounts, TestFile);
File.Exists(TestFile).Should().BeTrue();
File.ReadAllText(TestFile).Should().Be(@"
{
""AccountsSettings"": []
}
".Trim());
}
[TestMethod]
public void overwrite_existing_file()
{
File.Exists(TestFile).Should().BeFalse();
WriteToTestFile("foo");
File.Exists(TestFile).Should().BeTrue();
var accounts = new Accounts();
_ = new AccountsPersister(accounts, TestFile);
File.Exists(TestFile).Should().BeTrue();
File.ReadAllText(TestFile).Should().Be(@"
{
""AccountsSettings"": []
}
".Trim());
}
[TestMethod]
public void save_multiple_children()
{
var accounts = new Accounts();
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)) { }
var persister = new AccountsPersister(TestFile);
persister.Accounts.AccountsSettings.Count.Should().Be(2);
persister.Accounts.AccountsSettings[1].AccountName.Should().Be("n1");
}
[TestMethod]
public void save_with_identity()
{
var usLocale = Localization.Locales.Single(l => l.Name == "us");
var id = new Identity(usLocale);
var idJson = JsonConvert.SerializeObject(id, Identity.GetJsonSerializerSettings());
var accounts = new Accounts();
accounts.Add(new Account("a0") { AccountName = "n0", IdentityTokens = id });
// dispose to cease auto-updates
using (var p = new AccountsPersister(accounts, TestFile)) { }
var persister = new AccountsPersister(TestFile);
var acct = persister.Accounts.AccountsSettings[0];
acct.AccountName.Should().Be("n0");
acct.Locale.CountryCode.Should().Be("us");
}
}
[TestClass]
public class save : AccountsTestBase
{
// add/save account after file creation
[TestMethod]
public void save_1_account()
{
// create initial file
using (var p = new AccountsPersister(new Accounts(), TestFile)) { }
// load file. create account
using (var p = new AccountsPersister(TestFile))
{
var localeIn = Localization.Locales.Single(l => l.Name == "us");
var idIn = new Identity(localeIn);
var acctIn = new Account("a0") { AccountName = "n0", IdentityTokens = idIn };
p.Accounts.Add(acctIn);
}
// re-load file. ensure account still exists
using (var p = new AccountsPersister(TestFile))
{
p.Accounts.AccountsSettings.Count.Should().Be(1);
var acct0 = p.Accounts.AccountsSettings[0];
acct0.AccountName.Should().Be("n0");
acct0.Locale.CountryCode.Should().Be("us");
}
}
// add/save mult accounts after file creation
// separately create 2 accounts. ensure both still exist in the end
[TestMethod]
public void save_2_accounts()
{
// create initial file
using (var p = new AccountsPersister(new Accounts(), TestFile)) { }
// load file. create account 0
using (var p = new AccountsPersister(TestFile))
{
var localeIn = Localization.Locales.Single(l => l.Name == "us");
var idIn = new Identity(localeIn);
var acctIn = new Account("a0") { AccountName = "n0", IdentityTokens = idIn };
p.Accounts.Add(acctIn);
}
// re-load file. ensure account still exists
using (var p = new AccountsPersister(TestFile))
{
p.Accounts.AccountsSettings.Count.Should().Be(1);
var acct0 = p.Accounts.AccountsSettings[0];
acct0.AccountName.Should().Be("n0");
acct0.Locale.CountryCode.Should().Be("us");
}
// load file. create account 1
using (var p = new AccountsPersister(TestFile))
{
var localeIn = Localization.Locales.Single(l => l.Name == "uk");
var idIn = new Identity(localeIn);
var acctIn = new Account("a1") { AccountName = "n1", IdentityTokens = idIn };
p.Accounts.Add(acctIn);
}
// re-load file. ensure both accounts still exist
using (var p = new AccountsPersister(TestFile))
{
p.Accounts.AccountsSettings.Count.Should().Be(2);
var acct0 = p.Accounts.AccountsSettings[0];
acct0.AccountName.Should().Be("n0");
acct0.Locale.CountryCode.Should().Be("us");
var acct1 = p.Accounts.AccountsSettings[1];
acct1.AccountName.Should().Be("n1");
acct1.Locale.CountryCode.Should().Be("uk");
}
}
[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()
{
// 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);
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.Add(acct2);
}
// update AccountName on existing file
using (var p = new AccountsPersister(TestFile))
{
var acct0 = p.Accounts.AccountsSettings[0];
acct0.AccountName = "new";
}
// re-load file. ensure both accounts still exist
using (var p = new AccountsPersister(TestFile))
{
p.Accounts.AccountsSettings.Count.Should().Be(2);
var acct0 = p.Accounts.AccountsSettings[0];
// new
acct0.AccountName.Should().Be("new");
// still here
acct0.Locale.CountryCode.Should().Be("us");
var acct1 = p.Accounts.AccountsSettings[1];
acct1.AccountName.Should().Be("n1");
acct1.Locale.CountryCode.Should().Be("uk");
}
}
// update identity. must be non-destructive to all other data
[TestMethod]
public void replace_identity()
{
// 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);
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.Add(acct2);
}
// update identity on existing file
using (var p = new AccountsPersister(TestFile))
{
var locale = Localization.Locales.Single(l => l.Name == "uk");
var id = new Identity(locale);
var acct0 = p.Accounts.AccountsSettings[0];
acct0.IdentityTokens = id;
}
// re-load file. ensure both accounts still exist
using (var p = new AccountsPersister(TestFile))
{
p.Accounts.AccountsSettings.Count.Should().Be(2);
var acct0 = p.Accounts.AccountsSettings[0];
// new
acct0.Locale.CountryCode.Should().Be("uk");
// still here
acct0.AccountName.Should().Be("n0");
var acct1 = p.Accounts.AccountsSettings[1];
acct1.AccountName.Should().Be("n1");
acct1.Locale.CountryCode.Should().Be("uk");
}
}
// multi-level subscribe => update
// edit field of existing identity. must be non-destructive to all other data
[TestMethod]
public void update_identity_field()
{
// 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);
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.Add(acct2);
}
// update identity on existing file
using (var p = new AccountsPersister(TestFile))
{
p.Accounts.AccountsSettings[0]
.IdentityTokens
.Update(new AccessToken("Atna|_NEW_", DateTime.Now.AddDays(1)));
}
// re-load file. ensure both accounts still exist
using (var p = new AccountsPersister(TestFile))
{
p.Accounts.AccountsSettings.Count.Should().Be(2);
var acct0 = p.Accounts.AccountsSettings[0];
// new
acct0.IdentityTokens.ExistingAccessToken.TokenValue.Should().Be("Atna|_NEW_");
// still here
acct0.AccountName.Should().Be("n0");
acct0.Locale.CountryCode.Should().Be("us");
var acct1 = p.Accounts.AccountsSettings[1];
acct1.AccountName.Should().Be("n1");
acct1.Locale.CountryCode.Should().Be("uk");
}
}
}
[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);
}
}
}