Libation 4.0 prep: in process of migrating to new settings files

This commit is contained in:
Robert McRackan 2020-08-16 15:44:47 -04:00
parent 8391e43b03
commit 643ae09b2b
8 changed files with 667 additions and 17 deletions

View File

@ -0,0 +1,162 @@
using System;
using System.Collections.Generic;
using AudibleApi;
using AudibleApi.Authorization;
using Dinah.Core;
using Dinah.Core.IO;
using Newtonsoft.Json;
namespace InternalUtilities
{
public class AccountsPersister : JsonFilePersister<Accounts>
{
/// <summary>Alias for Target </summary>
public Accounts Accounts => Target;
/// <summary>uses path. create file if doesn't yet exist</summary>
public AccountsPersister(Accounts target, string path, string jsonPath = null)
: base(target, path, jsonPath) { }
/// <summary>load from existing file</summary>
public AccountsPersister(string path, string jsonPath = null)
: base(path, jsonPath) { }
protected override JsonSerializerSettings GetSerializerSettings()
=> Identity.GetJsonSerializerSettings();
}
public class Accounts : Updatable
{
public event EventHandler Updated;
private void update(object sender = null, EventArgs e = null)
=> Updated?.Invoke(this, new EventArgs());
public Accounts() { }
// for some reason this will make the json instantiator use _accountsSettings_json.set()
[JsonConstructor]
protected Accounts(List<Account> accounts) { }
#region AccountsSettings
private List<Account> _accountsSettings_backing = new List<Account>();
[JsonProperty(PropertyName = "AccountsSettings")]
private List<Account> _accountsSettings_json
{
get => _accountsSettings_backing;
// 'set' is only used by json deser
set
{
_accountsSettings_backing = value;
if (_accountsSettings_backing is null)
return;
foreach (var acct in _accountsSettings_backing)
acct.Updated += update;
update();
}
}
[JsonIgnore]
public IReadOnlyList<Account> AccountsSettings => _accountsSettings_json.AsReadOnly();
#endregion
public static Accounts FromJson(string json)
=> JsonConvert.DeserializeObject<Accounts>(json, Identity.GetJsonSerializerSettings());
public string ToJson(Formatting formatting = Formatting.Indented)
=> JsonConvert.SerializeObject(this, formatting, Identity.GetJsonSerializerSettings());
public void UNITTEST_Seed(Account account)
{
_accountsSettings_backing.Add(account);
update();
}
// replace UNITTEST_Seed
// when creating Account object (get, update, insert), subscribe to it's update. including all new ones on initial load
// removing: unsubscribe
// IEnumerable<Account> GetAllAccounts
// 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?
// 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 ???
}
public class Account : Updatable
{
public event EventHandler Updated;
private void update(object sender = null, EventArgs e = null)
=> Updated?.Invoke(this, new EventArgs());
// canonical. immutable. email or phone number
public string AccountId { get; }
// user-friendly, non-canonical name. mutable
private string _accountName;
public string AccountName
{
get => _accountName;
set
{
if (string.IsNullOrWhiteSpace(value))
return;
var v = value.Trim();
if (v == _accountName)
return;
_accountName = v;
update();
}
}
private string _decryptKey = "";
public string DecryptKey
{
get => _decryptKey;
set
{
var v = (value ?? "").Trim();
if (v == _decryptKey)
return;
_decryptKey = v;
update();
}
}
private Identity _identity;
public Identity IdentityTokens
{
get => _identity;
set
{
if (_identity is null && value is null)
return;
if (_identity != null)
_identity.Updated -= update;
if (value != null)
value.Updated += update;
_identity = value;
update();
}
}
[JsonIgnore]
public Locale Locale => IdentityTokens?.Locale;
public Account(string accountId)
{
ArgumentValidator.EnsureNotNullOrWhiteSpace(accountId, nameof(accountId));
AccountId = accountId.Trim();
}
}
}

View File

@ -12,11 +12,11 @@ namespace InternalUtilities
{ {
public static class AudibleApiActions public static class AudibleApiActions
{ {
public static async Task<Api> GetApiAsyncLegacy30(ILoginCallback loginCallback = null) public static async Task<Api> GetApiAsyncLegacy30Async()
{ {
Localization.SetLocale(Configuration.Instance.LocaleCountryCode); Localization.SetLocale(Configuration.Instance.LocaleCountryCode);
return await EzApiCreator.GetApiAsync(AudibleApiStorage.AccountsSettingsFileLegacy30, null, loginCallback); return await EzApiCreator.GetApiAsync(AudibleApiStorage.AccountsSettingsFileLegacy30);
} }
/// <summary>USE THIS from within Libation. It wraps the call with correct JSONPath</summary> /// <summary>USE THIS from within Libation. It wraps the call with correct JSONPath</summary>

View File

@ -14,6 +14,15 @@ namespace InternalUtilities
public static string AccountsSettingsFile => Path.Combine(Configuration.Instance.LibationFiles, "AccountsSettings.json"); public static string AccountsSettingsFile => Path.Combine(Configuration.Instance.LibationFiles, "AccountsSettings.json");
public static void EnsureAccountsSettingsFileExists()
{
if (File.Exists(AccountsSettingsFile))
return;
// saves. BEWARE: this will overwrite an existing file
_ = new AccountsPersister(new Accounts(), AccountsSettingsFile);
}
// TEMP // TEMP
public static string GetJsonPath() => null; public static string GetJsonPath() => null;

View File

@ -82,6 +82,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsDesktopUtilities", "
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibationLauncher", "LibationLauncher\LibationLauncher.csproj", "{F3B04A3A-20C8-4582-A54A-715AF6A5D859}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LibationLauncher", "LibationLauncher\LibationLauncher.csproj", "{F3B04A3A-20C8-4582-A54A-715AF6A5D859}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "0 Libation Tests", "0 Libation Tests", "{67E66E82-5532-4440-AFB3-9FB1DF9DEF53}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InternalUtilities.Tests", "_Tests\InternalUtilities.Tests\InternalUtilities.Tests.csproj", "{8447C956-B03E-4F59-9DD4-877793B849D9}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -200,6 +204,10 @@ Global
{F3B04A3A-20C8-4582-A54A-715AF6A5D859}.Debug|Any CPU.Build.0 = Debug|Any CPU {F3B04A3A-20C8-4582-A54A-715AF6A5D859}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F3B04A3A-20C8-4582-A54A-715AF6A5D859}.Release|Any CPU.ActiveCfg = Release|Any CPU {F3B04A3A-20C8-4582-A54A-715AF6A5D859}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F3B04A3A-20C8-4582-A54A-715AF6A5D859}.Release|Any CPU.Build.0 = Release|Any CPU {F3B04A3A-20C8-4582-A54A-715AF6A5D859}.Release|Any CPU.Build.0 = Release|Any CPU
{8447C956-B03E-4F59-9DD4-877793B849D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8447C956-B03E-4F59-9DD4-877793B849D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8447C956-B03E-4F59-9DD4-877793B849D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8447C956-B03E-4F59-9DD4-877793B849D9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -233,6 +241,7 @@ Global
{059CE32C-9AD6-45E9-A166-790DFFB0B730} = {43E3ACB3-E0BC-4370-8DBB-E3720C8C8FD1} {059CE32C-9AD6-45E9-A166-790DFFB0B730} = {43E3ACB3-E0BC-4370-8DBB-E3720C8C8FD1}
{E7EFD64D-6630-4426-B09C-B6862A92E3FD} = {F0CBB7A7-D3FB-41FF-8F47-CF3F6A592249} {E7EFD64D-6630-4426-B09C-B6862A92E3FD} = {F0CBB7A7-D3FB-41FF-8F47-CF3F6A592249}
{F3B04A3A-20C8-4582-A54A-715AF6A5D859} = {8679CAC8-9164-4007-BDD2-F004810EDA14} {F3B04A3A-20C8-4582-A54A-715AF6A5D859} = {8679CAC8-9164-4007-BDD2-F004810EDA14}
{8447C956-B03E-4F59-9DD4-877793B849D9} = {67E66E82-5532-4440-AFB3-9FB1DF9DEF53}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {615E00ED-BAEF-4E8E-A92A-9B82D87942A9} SolutionGuid = {615E00ED-BAEF-4E8E-A92A-9B82D87942A9}

View File

@ -13,7 +13,7 @@
<!-- <PublishSingleFile>true</PublishSingleFile> --> <!-- <PublishSingleFile>true</PublishSingleFile> -->
<RuntimeIdentifier>win-x64</RuntimeIdentifier> <RuntimeIdentifier>win-x64</RuntimeIdentifier>
<Version>3.1.12.95</Version> <Version>3.1.12.120</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -24,7 +24,7 @@ namespace LibationLauncher
createSettings(); createSettings();
ensureIdentityFile(); AudibleApiStorage.EnsureAccountsSettingsFileExists();
migrateIdentityFile(); migrateIdentityFile();
updateSettingsFile(); updateSettingsFile();
@ -82,18 +82,6 @@ namespace LibationLauncher
Environment.Exit(0); Environment.Exit(0);
} }
private static void ensureIdentityFile()
{
if (File.Exists(AudibleApiStorage.AccountsSettingsFile))
return;
var jObj = new JObject {
{ "AccountsSettings", new JArray() }
};
var contents = jObj.ToString(Formatting.Indented);
File.WriteAllText(AudibleApiStorage.AccountsSettingsFile, contents);
}
private static void migrateIdentityFile() private static void migrateIdentityFile()
{ {
if (!File.Exists(AudibleApiStorage.AccountsSettingsFileLegacy30)) if (!File.Exists(AudibleApiStorage.AccountsSettingsFileLegacy30))
@ -102,8 +90,9 @@ namespace LibationLauncher
try try
{ {
// //
// for all in here: read directly from json file => JObject. A lot of this is legacy; don't rely on applicable POCOs // in here: read directly from json file => JObject. A lot of this is legacy; don't rely on applicable POCOs
// //
var legacyContents = File.ReadAllText(AudibleApiStorage.AccountsSettingsFileLegacy30); var legacyContents = File.ReadAllText(AudibleApiStorage.AccountsSettingsFileLegacy30);
var legacyJObj = JObject.Parse(legacyContents); var legacyJObj = JObject.Parse(legacyContents);
@ -127,6 +116,11 @@ namespace LibationLauncher
} }
} }
// create new account stub in new file
var api = AudibleApiActions.GetApiAsyncLegacy30Async().GetAwaiter().GetResult();
var email = api.GetEmailAsync().GetAwaiter().GetResult();
var locale = api.GetLocaleAsync(AudibleApi.CustomerOptions.All).GetAwaiter().GetResult();
// more to do? // more to do?
} }

View File

@ -0,0 +1,451 @@
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 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 AccountsPersisterTestBase
{
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 : AccountsPersisterTestBase
{
[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.UNITTEST_Seed(new Account("a0") { AccountName = "n0" });
accounts.UNITTEST_Seed(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.UNITTEST_Seed(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 : AccountsPersisterTestBase
{
// 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.UNITTEST_Seed(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.UNITTEST_Seed(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.UNITTEST_Seed(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");
}
}
// 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.UNITTEST_Seed(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);
}
// 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.UNITTEST_Seed(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);
}
// 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.UNITTEST_Seed(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);
}
// 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");
}
}
}
}

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />
<PackageReference Include="MSTest.TestFramework" Version="2.1.2" />
<PackageReference Include="coverlet.collector" Version="1.3.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\audible api\AudibleApi\_Tests\AudibleApi.Tests\AudibleApi.Tests.csproj" />
<ProjectReference Include="..\..\..\Dinah.Core\_Tests\TestCommon\TestCommon.csproj" />
<ProjectReference Include="..\..\InternalUtilities\InternalUtilities.csproj" />
</ItemGroup>
</Project>