Skip to content

Commit

Permalink
Refactor and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
vanessagertman committed Apr 26, 2017
1 parent 7ada9a5 commit 2609164
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 51 deletions.
1 change: 1 addition & 0 deletions src/Magpie/Magpie.Tests/Magpie.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
<Compile Include="Models\HelpersTest.cs" />
<Compile Include="Services\MagpieTest.cs" />
<Compile Include="Services\UpdateDeciderTest.cs" />
<Compile Include="ViewModels\EnrollmentViewModelTest.cs" />
<Compile Include="ViewModels\MainWindowViewModelTest.cs" />
<Compile Include="Mocks\MockMagpie.cs" />
<Compile Include="Mocks\MockMainWindowViewModel.cs" />
Expand Down
4 changes: 3 additions & 1 deletion src/Magpie/Magpie.Tests/Mocks/MockChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ namespace Magpie.Tests.Mocks
{
internal class MockChannel : Channel
{
internal MockChannel(int id, string version = null)
internal MockChannel(int id, string version = null, string build = null, string enrollmentEula = null)
{
Id = id;
if (version != null)
{
Version = new Version(version);
}
Build = build;
EnrollmentEulaUrl = enrollmentEula;
}

internal void SetArtifactUrl(string url)
Expand Down
16 changes: 13 additions & 3 deletions src/Magpie/Magpie.Tests/Mocks/MockMagpie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@ namespace Magpie.Tests.Mocks
internal class MockMagpie : MagpieUpdater.Services.Magpie
{
private readonly string VALID_JSON =
@"{'foo': 'bar', 'channels': [{ 'id': 2, 'version': '5.8.8', 'release_notes_url': 'release_notes_url_http', 'artifact_url': 'artifact_url_http', 'build_date': '01/28/2017'}, { 'id': 3, 'version': '6.8.8', 'release_notes_url': 'release_notes_url_http', 'artifact_url': 'artifact_url_http', 'build_date': '01/28/2017'}]}"
@"{'foo': 'bar', 'channels': [{ 'id': 2, 'version': '5.8.8', 'release_notes_url': 'release_notes_url_http', 'artifact_url': 'artifact_url_http', 'build_date': '01/28/2017'}, { 'id': 3, 'version': '6.8.8', 'release_notes_url': 'release_notes_url_http', 'artifact_url': 'artifact_url_http', 'build_date': '01/28/2017'}, { 'id': 4, 'version': '7.8.8', 'release_notes_url': 'release_notes_url_http', 'artifact_url': 'artifact_url_http', 'build_date': '01/28/2017', 'requires_enrollment': 'true', 'enrollment_eula_url': 'enrollment_url'}]}"
.MakeJson();


internal RemoteAppcast RemoteAppcast { get; private set; }
internal bool _showUpdateWindowFlag;
internal bool _showNoUpdatesWindowFlag;
internal IRemoteContentDownloader _remoteContentDownloader;
internal bool _showEnrollmentWindow;
internal Enrollment _enrollmentToReturn;

public MockMagpie(string validUrl, IDebuggingInfoLogger infoLogger = null)
: base(new AppInfo(validUrl), infoLogger)
public MockMagpie(string validUrl, IDebuggingInfoLogger infoLogger = null, IAnalyticsLogger analyticsLogger = null)
: base(new AppInfo(validUrl), infoLogger, analyticsLogger)
{
_remoteContentDownloader = Substitute.For<IRemoteContentDownloader>();
_remoteContentDownloader.DownloadStringContent(validUrl, Arg.Any<IDebuggingInfoLogger>()).Returns(Task.FromResult(VALID_JSON));
Expand Down Expand Up @@ -47,5 +50,12 @@ protected override void ShowNoUpdatesWindow()
protected override void ShowErrorWindow()
{
}

protected override void ShowEnrollmentWindow(Enrollment enrollment)
{
_showEnrollmentWindow = true;
enrollment.IsEnrolled = _enrollmentToReturn.IsEnrolled;
enrollment.Email = _enrollmentToReturn.Email;
}
}
}
64 changes: 63 additions & 1 deletion src/Magpie/Magpie.Tests/Services/MagpieTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ namespace Magpie.Tests.Services
public class MagpieTest
{
private MockMagpie _mockMagpie;
private IAnalyticsLogger _analyticsLogger;

[TestInitialize]
public void Initialize()
{
AssemblyInjector.Inject();
_mockMagpie = new MockMagpie("validContentUrl");
_analyticsLogger = Substitute.For<IAnalyticsLogger>();
_mockMagpie = new MockMagpie("validContentUrl", analyticsLogger: _analyticsLogger);
}

[TestMethod]
Expand Down Expand Up @@ -109,5 +111,65 @@ public void SwitchingSubscribedThenCallingForceUpdateChecksNewChannel()
_mockMagpie.ForceCheckInBackground();
updateDecider.Received(2).ShouldUpdate(Arg.Is<Channel>(e => e.Id == 3), true);
}

[TestMethod]
public void SwitchingToChannelThatDoesNotRequireEnrollment_ChecksForUpdates()
{
var updateDecider = Substitute.For<UpdateDecider>(new DebuggingWindowViewModel());
updateDecider.ShouldUpdate(Arg.Any<Channel>(), true).Returns(false);
_mockMagpie.UpdateDecider = updateDecider;
_mockMagpie.SwitchSubscribedChannel(3);

Assert.IsFalse(_mockMagpie._showEnrollmentWindow);
updateDecider.Received(1).ShouldUpdate(Arg.Is<Channel>(e => e.Id == 3), true);
}

[TestMethod]
public void SwitchingToChannelThatRequiresEnrollment_ShowsEnrollmentWindow()
{
_mockMagpie.SwitchSubscribedChannel(4);
Assert.IsTrue(_mockMagpie._showEnrollmentWindow);
}

[TestMethod]
public void FailingToEnroll_DoesNotCheckForUpdates()
{
var updateDecider = Substitute.For<UpdateDecider>(new DebuggingWindowViewModel());
_mockMagpie.UpdateDecider = updateDecider;
_mockMagpie._enrollmentToReturn = new Enrollment(new Channel()){IsEnrolled = false};
_mockMagpie.SwitchSubscribedChannel(4);

Assert.IsTrue(_mockMagpie._showEnrollmentWindow);
updateDecider.DidNotReceive().ShouldUpdate(Arg.Any<Channel>(), Arg.Any<bool>());
}

[TestMethod]
public void SuccessfullyEnrolled_CheckForUpdates()
{
var updateDecider = Substitute.For<UpdateDecider>(new DebuggingWindowViewModel());
_mockMagpie.UpdateDecider = updateDecider;
updateDecider.ShouldUpdate(Arg.Any<Channel>(), true).Returns(false);
_mockMagpie._enrollmentToReturn = new Enrollment(new Channel()) { IsEnrolled = true };
_mockMagpie.SwitchSubscribedChannel(4);

Assert.IsTrue(_mockMagpie._showEnrollmentWindow);
updateDecider.Received(1).ShouldUpdate(Arg.Is<Channel>(e => e.Id == 4), true);
}

[TestMethod]
public void SwitchChannel_LogsEnrollment()
{
_mockMagpie.SwitchSubscribedChannel(3);
_analyticsLogger.Received(1).LogEnrollment(Arg.Any<Enrollment>());
}

[TestMethod]
public void SwitchChannel_EnrollmentAvailableEventGetsFired()
{
var raised = false;
_mockMagpie.EnrollmentAvailableEvent += (s, a) => { raised = true; };
_mockMagpie.SwitchSubscribedChannel(3);
Assert.IsTrue(raised);
}
}
}
63 changes: 63 additions & 0 deletions src/Magpie/Magpie.Tests/ViewModels/EnrollmentViewModelTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using Magpie.Tests.Mocks;
using MagpieUpdater.Models;
using MagpieUpdater.Services;
using MagpieUpdater.ViewModels;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Magpie.Tests.ViewModels
{
[TestClass]
public class EnrollmentViewModelTest
{
[TestMethod]
public void PropertiesAreInitializedCorrectly()
{
var channel = new MockChannel(1, build: "Test Build", enrollmentEula: "www.example.com");
var enrollment = new Enrollment(channel);
var appInfo = new AppInfo("testString") {AppIconPath = "iconPath"};
var sut = new EnrollmentViewModel(enrollment, appInfo);
Assert.AreEqual("Test Build", sut.ChannelName);
Assert.AreEqual("www.example.com", sut.EnrollmentEulaUrl);
Assert.AreEqual("iconPath", sut.AppIconPath);
}

[TestMethod]
public void CanNotEnrollWithInvalidEmail()
{
var sut = new EnrollmentViewModel(new Enrollment(new Channel()), new AppInfo("testString"))
{
EmailAddress = "test"
};
Assert.IsFalse(sut.EnrollCommand.CanExecute(null));
sut.EmailAddress = null;
Assert.IsFalse(sut.EnrollCommand.CanExecute(null));
sut.EmailAddress = string.Empty;
Assert.IsFalse(sut.EnrollCommand.CanExecute(null));
sut.EmailAddress = "test@";
Assert.IsFalse(sut.EnrollCommand.CanExecute(null));
}

[TestMethod]
public void CanEnrollWithValidEmail()
{
var sut = new EnrollmentViewModel(new Enrollment(new Channel()), new AppInfo("testString"))
{
EmailAddress = "[email protected]"
};
Assert.IsTrue(sut.EnrollCommand.CanExecute(null));
}

[TestMethod]
public void ExecuteEnrollCommand_FillsInEnrollment()
{
var enrollment = new Enrollment(new Channel());
var sut = new EnrollmentViewModel(enrollment, new AppInfo("testString"))
{
EmailAddress = "[email protected]"
};
sut.EnrollCommand.Execute(null);
Assert.IsTrue(enrollment.IsEnrolled);
Assert.AreEqual("[email protected]", enrollment.Email);
}
}
}
8 changes: 7 additions & 1 deletion src/Magpie/Magpie/Interfaces/IAnalyticsLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,14 @@ public interface IAnalyticsLogger

/// <summary>
/// Log analytics when update is available.
/// <param name="channel">Channel that has the latest update available.</param>
/// <param name="channel">Channel that has the latest update available</param>
/// </summary>
void LogUpdateAvailable(Channel channel);

/// <summary>
/// Log enrollment status.
/// </summary>
/// <param name="enrollment">Enrollment status</param>
void LogEnrollment(Enrollment enrollment);
}
}
4 changes: 2 additions & 2 deletions src/Magpie/Magpie/Models/Channel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class Channel
public int Id { get; protected set; }

[DataMember(Name = "build", IsRequired = false)]
public string Build { get; private set; }
public string Build { get; protected set; }

[DataMember(Name = "version", IsRequired = true)] private string _version;
public Version Version { get; protected set; }
Expand All @@ -31,7 +31,7 @@ public class Channel
public bool RequiresEnrollment { get; private set; }

[DataMember(Name = "enrollment_eula_url", IsRequired = false)]
public string EnrollmentEulaUrl { get; private set; }
public string EnrollmentEulaUrl { get; protected set; }

// Dates example:
// e.g. January 30, 2015 18:15:00 +0200
Expand Down
4 changes: 4 additions & 0 deletions src/Magpie/Magpie/Services/AnalyticsLogger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,9 @@ public virtual void LogUpdateCancelled()
public virtual void LogUpdateAvailable(Channel channel)
{
}

public void LogEnrollment(Enrollment enrollment)
{
}
}
}
41 changes: 17 additions & 24 deletions src/Magpie/Magpie/Services/Magpie.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class Magpie : ISoftwareUpdater
internal IRemoteContentDownloader RemoteContentDownloader { get; set; }
public event EventHandler<SingleEventArgs<RemoteAppcast>> RemoteAppcastAvailableEvent;
public event EventHandler<SingleEventArgs<string>> ArtifactDownloadedEvent;
public event EventHandler<SingleEventArgs<Enrollment>> EnrollmentAvailableEvent;

public Magpie(AppInfo appInfo, IDebuggingInfoLogger debuggingInfoLogger = null,
IAnalyticsLogger analyticsLogger = null)
Expand Down Expand Up @@ -60,30 +61,20 @@ private async Task Check(string appcastUrl, CheckState checkState, int channelId
var data = await RemoteContentDownloader.DownloadStringContent(appcastUrl, _logger).ConfigureAwait(true);
if (string.IsNullOrWhiteSpace(data))
{
if (checkState == CheckState.Force)
if (checkState == CheckState.Force || checkState == CheckState.ChannelSwitch)
{
ShowErrorWindow();
}
return;
}

var appcast = ParseAppcast(data);
OnRemoteAppcastAvailableEvent(new SingleEventArgs<RemoteAppcast>(appcast));

if (checkState == CheckState.ChannelSwitch)
{
var enrollment = Enroll(appcast, channelId);
// OnEnrolledEvent(enrollment);
if (enrollment.IsRequired && !enrollment.IsEnrolled)
{
// todo: return false
return;
}
}
if (checkState == CheckState.ChannelSwitch && FailedToEnroll(appcast, channelId)) return;

var channelToUpdateFrom = BestChannelFinder.Find(channelId, appcast.Channels);

if (UpdateDecider.ShouldUpdate(channelToUpdateFrom, checkState == CheckState.Force))
if (UpdateDecider.ShouldUpdate(channelToUpdateFrom, checkState == CheckState.Force || checkState == CheckState.ChannelSwitch))
{
_analyticsLogger.LogUpdateAvailable(channelToUpdateFrom);
await ShowUpdateWindow(channelToUpdateFrom);
Expand All @@ -103,23 +94,18 @@ private async Task Check(string appcastUrl, CheckState checkState, int channelId
}
}

private Enrollment Enroll(RemoteAppcast appcast, int channelId)
private bool FailedToEnroll(RemoteAppcast appcast, int channelId)
{
var channel = appcast.Channels.FirstOrDefault(c => c.Id == channelId);
var enrollment = new Enrollment(channel);
if (channel == null)
{
return enrollment;
}

enrollment.IsRequired = channel.RequiresEnrollment;
if (enrollment.IsRequired)
if (channel != null && channel.RequiresEnrollment)
{
enrollment.IsRequired = true;
ShowEnrollmentWindow(enrollment);
}

// check enrollment
return enrollment;
_analyticsLogger.LogEnrollment(enrollment);
OnEnrollmentAvailableEvent(new SingleEventArgs<Enrollment>(enrollment));
return enrollment.IsRequired && !enrollment.IsEnrolled;
}

protected virtual void ShowEnrollmentWindow(Enrollment enrollment)
Expand Down Expand Up @@ -262,6 +248,7 @@ private RemoteAppcast ParseAppcast(string content)
_logger.Log("Started deserializing remote channel content");
var appcast = RemoteAppcast.MakeFromJson(content);
_logger.Log("Finished deserializing remote channel content");
OnRemoteAppcastAvailableEvent(new SingleEventArgs<RemoteAppcast>(appcast));
return appcast;
}

Expand All @@ -276,5 +263,11 @@ protected virtual void OnArtifactDownloadedEvent(SingleEventArgs<string> args)
var handler = ArtifactDownloadedEvent;
if (handler != null) handler(this, args);
}

protected virtual void OnEnrollmentAvailableEvent(SingleEventArgs<Enrollment> args)
{
var handler = EnrollmentAvailableEvent;
if (handler != null) handler(this, args);
}
}
}
22 changes: 3 additions & 19 deletions src/Magpie/Magpie/ViewModels/EnrollmentViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Mail;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using MagpieUpdater.Models;
using MagpieUpdater.Properties;
using MagpieUpdater.Services;

namespace MagpieUpdater.ViewModels
Expand All @@ -30,7 +24,6 @@ public string EmailAddress
get { return _emailAddress; }
set
{
_emailAddress = value;
SetProperty(ref _emailAddress, value);
EnrollCommand.RaiseCanExecuteChanged();
}
Expand All @@ -39,25 +32,16 @@ public string EmailAddress
public string ChannelName
{
get { return _channelName; }
set
{
_channelName = value;
SetProperty(ref _channelName, value);

}
set { SetProperty(ref _channelName, value); }
}

public string EnrollmentEulaUrl
{
get { return _enrollmentEulaUrl; }
set
{
_enrollmentEulaUrl = value;
SetProperty(ref _enrollmentEulaUrl, value);
}
set { SetProperty(ref _enrollmentEulaUrl, value); }
}

public DelegateCommand EnrollCommand { get; set; }
public DelegateCommand EnrollCommand { get; private set; }

public EnrollmentViewModel(Enrollment enrollment, AppInfo appInfo)
{
Expand Down

0 comments on commit 2609164

Please sign in to comment.