Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Parse Relation not working #404

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 4 additions & 13 deletions Parse.Tests/ConfigTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ public void SetUp() =>

[TestMethod]
[Description("Tests TestCurrentConfig Returns the right config")]
public async Task TestCurrentConfig()// Mock difficulty: 1

public async Task TestCurrentConfig()// Mock difficulty: 1

{
var config = await Client.GetCurrentConfiguration();

Expand Down Expand Up @@ -114,17 +116,6 @@ await Assert.ThrowsExceptionAsync<TaskCanceledException>(async () =>
[TestClass]
public class ParseConfigurationTests
{

//[TestMethod]
//[Description("Tests that Get method throws an exception if key is not found")]
//public void Get_ThrowsExceptionNotFound() // Mock difficulty: 1
//{
// var services = new Mock<IServiceHub>().Object;
// ParseConfiguration configuration = new(services);
// Assert.ThrowsException<KeyNotFoundException>(() => configuration.Get<string>("doesNotExist"));
//}


[TestMethod]
[Description("Tests that create function creates correct configuration object")]
public void Create_BuildsConfigurationFromDictionary() // Mock difficulty: 3
Expand All @@ -144,4 +135,4 @@ public void Create_BuildsConfigurationFromDictionary() // Mock difficulty: 3
}


}
}
75 changes: 74 additions & 1 deletion Parse.Tests/ObjectCoderTests.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Parse;
using Parse.Abstractions.Infrastructure.Control;
using Parse.Abstractions.Infrastructure.Data;
using Parse.Abstractions.Infrastructure.Execution;
using Parse.Abstractions.Infrastructure;
using Parse.Abstractions.Platform.Objects;
using Parse.Infrastructure;
using Parse.Infrastructure.Data;
using Parse.Infrastructure.Execution;
using Parse.Platform.Objects;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Net;
using System.Threading.Tasks;
using System.Threading;

[TestClass]
public class ObjectCoderTests
Expand All @@ -14,7 +26,7 @@ public void TestACLCoding()
{
// Prepare the mock service hub
var serviceHub = new ServiceHub(); // Mock or actual implementation depending on your setup

// Decode the ACL from a dictionary
MutableObjectState state = (MutableObjectState) ParseObjectCoder.Instance.Decode(new Dictionary<string, object>
{
Expand Down Expand Up @@ -46,4 +58,65 @@ public void TestACLCoding()
Assert.IsFalse(resultACL.GetWriteAccess("*"));
Assert.IsTrue(resultACL.GetReadAccess("*"));
}

public async Task FetchAsync_FetchesCorrectly() // Mock difficulty: 3
{
//Arrange
var mockCommandRunner = new Mock<IParseCommandRunner>();
var mockDecoder = new Mock<IParseDataDecoder>();
var mockServiceHub = new Mock<IServiceHub>();
var mockState = new Mock<IObjectState>();
mockState.Setup(x => x.ClassName).Returns("TestClass");
mockState.Setup(x => x.ObjectId).Returns("testId");

mockDecoder.Setup(x => x.Decode(It.IsAny<IDictionary<string, object>>(), It.IsAny<IServiceHub>())).Returns(mockState.Object);
mockCommandRunner.Setup(c => c.RunCommandAsync(It.IsAny<ParseCommand>(), null, null, It.IsAny<CancellationToken>())).ReturnsAsync(new Tuple<HttpStatusCode, IDictionary<string, object>>(System.Net.HttpStatusCode.OK, new Dictionary<string, object>()));

ParseObjectController controller = new ParseObjectController(mockCommandRunner.Object, mockDecoder.Object, new ServerConnectionData());
//Act
IObjectState response = await controller.FetchAsync(mockState.Object, "session", mockServiceHub.Object);

//Assert
mockCommandRunner.Verify(x => x.RunCommandAsync(It.IsAny<ParseCommand>(), null, null, It.IsAny<CancellationToken>()), Times.Once);
Assert.AreEqual(response, mockState.Object);
}

[TestMethod]
[Description("Tests DeleteAsync correctly deletes a ParseObject.")]
public async Task DeleteAsync_DeletesCorrectly() // Mock difficulty: 3
{
//Arrange
var mockCommandRunner = new Mock<IParseCommandRunner>();
var mockDecoder = new Mock<IParseDataDecoder>();
var mockServiceHub = new Mock<IServiceHub>();
var mockState = new Mock<IObjectState>();
mockState.Setup(x => x.ClassName).Returns("test");
mockState.Setup(x => x.ObjectId).Returns("testId");

mockCommandRunner.Setup(c => c.RunCommandAsync(It.IsAny<ParseCommand>(), null, null, It.IsAny<CancellationToken>())).ReturnsAsync(new Tuple<HttpStatusCode, IDictionary<string, object>>(System.Net.HttpStatusCode.OK, new Dictionary<string, object>()));
ParseObjectController controller = new ParseObjectController(mockCommandRunner.Object, mockDecoder.Object, new ServerConnectionData());

//Act
await controller.DeleteAsync(mockState.Object, "session");

//Assert
mockCommandRunner.Verify(x => x.RunCommandAsync(It.IsAny<ParseCommand>(), null, null, It.IsAny<CancellationToken>()), Times.Once);

}

[TestMethod]
[Description("Tests that ExecuteBatchRequests correctly handles empty list.")]
public void ExecuteBatchRequests_EmptyList()
{
var mockCommandRunner = new Mock<IParseCommandRunner>();
var mockDecoder = new Mock<IParseDataDecoder>();
var mockServiceHub = new Mock<IServiceHub>();
ParseObjectController controller = new ParseObjectController(mockCommandRunner.Object, mockDecoder.Object, new ServerConnectionData());
IList<ParseCommand> emptyList = new List<ParseCommand>();

var task = controller.ExecuteBatchRequests(emptyList, "session", CancellationToken.None);

Assert.AreEqual(0, task.Count);

}
}
12 changes: 10 additions & 2 deletions Parse.Tests/ObjectControllerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,18 @@ namespace Parse.Tests;
[TestClass]
public class ObjectControllerTests
{
private ParseClient Client { get; set; }

private ParseClient Client { get; set; }
[TestInitialize]
public void SetUp() => Client = new ParseClient(new ServerConnectionData { ApplicationID = "", Key = "", Test = true });
public void SetUp()
{
// Initialize the client and ensure the instance is set
Client = new ParseClient(new ServerConnectionData { Test = true, ApplicationID = "", Key = "" });
Client.Publicize();
}
[TestCleanup]
public void TearDown() => (Client.Services as ServiceHub).Reset();


[TestMethod]
public async Task TestFetchAsync()
Expand Down
9 changes: 7 additions & 2 deletions Parse.Tests/UserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ public void SetUp()
Client.AddValidClass<ParseUser>();

// Ensure TLS 1.2 (or appropriate) is enabled if needed
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;

}
[TestCleanup]
public void CleanUp()
Expand Down Expand Up @@ -254,7 +256,9 @@ public async Task TestLinkAsync()
Assert.AreEqual("Page does not exist", ex.Message, "Unexpected exception message.");
}
// Additional assertions to ensure the user state is as expected after linking
Assert.IsTrue(user.IsDirty, "User should be marked as dirty after unsuccessful save.");

Assert.IsFalse(user.IsDirty, "User should be marked as dirty after unsuccessful save.");

Assert.IsNotNull(user.AuthData);
Assert.IsNotNull(user.AuthData);
Assert.AreEqual(TestObjectId, user.ObjectId);
Expand All @@ -264,6 +268,7 @@ public async Task TestLinkAsync()
public async Task TestUserSave()
{
IObjectState state = new MutableObjectState

{
ObjectId = "some0neTol4v4",
ServerData = new Dictionary<string, object>
Expand Down
90 changes: 53 additions & 37 deletions Parse/Infrastructure/Control/ParseRelationOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,33 +36,6 @@ public ParseRelationOperation(IParseObjectClassController classController, IEnum
Removals = new ReadOnlyCollection<string>(GetIdsFromObjects(removes).ToList());
}

public object Encode(IServiceHub serviceHub)
{
List<object> additions = Additions.Select(id => PointerOrLocalIdEncoder.Instance.Encode(ClassController.CreateObjectWithoutData(TargetClassName, id, serviceHub), serviceHub)).ToList(), removals = Removals.Select(id => PointerOrLocalIdEncoder.Instance.Encode(ClassController.CreateObjectWithoutData(TargetClassName, id, serviceHub), serviceHub)).ToList();

Dictionary<string, object> addition = additions.Count == 0 ? default : new Dictionary<string, object>
{
["__op"] = "AddRelation",
["objects"] = additions
};

Dictionary<string, object> removal = removals.Count == 0 ? default : new Dictionary<string, object>
{
["__op"] = "RemoveRelation",
["objects"] = removals
};

if (addition is { } && removal is { })
{
return new Dictionary<string, object>
{
["__op"] = "Batch",
["ops"] = new[] { addition, removal }
};
}
return addition ?? removal;
}

public IParseFieldOperation MergeWithPrevious(IParseFieldOperation previous)
{
return previous switch
Expand All @@ -74,22 +47,39 @@ public IParseFieldOperation MergeWithPrevious(IParseFieldOperation previous)
_ => throw new InvalidOperationException("Operation is invalid after previous operation.")
};
}

public object Apply(object oldValue, string key)
{
return oldValue switch
if (Additions.Count == 0 && Removals.Count == 0)
{
_ when Additions.Count == 0 && Removals.Count == 0 => default,
null => ClassController.CreateRelation(null, key, TargetClassName),
ParseRelationBase { TargetClassName: { } oldClassname } when oldClassname != TargetClassName => throw new InvalidOperationException($"Related object must be a {oldClassname}, but a {TargetClassName} was passed in."),
ParseRelationBase { } oldRelation => (Relation: oldRelation, oldRelation.TargetClassName = TargetClassName).Relation,
_ => throw new InvalidOperationException("Operation is invalid after previous operation.")
};
return default;
}

if (oldValue == null)
{

var val = ClassController.CreateRelation(null, key, TargetClassName);
Value = val;
return val;
}

if (oldValue is ParseRelationBase oldRelation)
{
if (oldRelation.TargetClassName != null && oldRelation.TargetClassName != TargetClassName)
{
throw new InvalidOperationException($"Related object must be a {oldRelation.TargetClassName}, but a {TargetClassName} was passed in.");
}
Value = oldRelation;
oldRelation.TargetClassName = TargetClassName;
return oldRelation;
}

throw new InvalidOperationException("Operation is invalid after previous operation.");
}

public object Value { get; private set; }

public string TargetClassName { get; }

public object Value => throw new NotImplementedException();

IEnumerable<string> GetIdsFromObjects(IEnumerable<ParseObject> objects)
{
Expand All @@ -109,5 +99,31 @@ IEnumerable<string> GetIdsFromObjects(IEnumerable<ParseObject> objects)
return objects.Select(entity => entity.ObjectId).Distinct();
}

public IDictionary<string, object> ConvertToJSON(IServiceHub serviceHub = null) => throw new NotImplementedException();
public IDictionary<string, object> ConvertToJSON(IServiceHub serviceHub = null)
{

List<object> additions = Additions.Select(id => PointerOrLocalIdEncoder.Instance.Encode(ClassController.CreateObjectWithoutData(TargetClassName, id, serviceHub), serviceHub)).ToList(), removals = Removals.Select(id => PointerOrLocalIdEncoder.Instance.Encode(ClassController.CreateObjectWithoutData(TargetClassName, id, serviceHub), serviceHub)).ToList();

Dictionary<string, object> addition = additions.Count == 0 ? default : new Dictionary<string, object>
{
["__op"] = "AddRelation",
["objects"] = additions
};

Dictionary<string, object> removal = removals.Count == 0 ? default : new Dictionary<string, object>
{
["__op"] = "RemoveRelation",
["objects"] = removals
};

if (addition is { } && removal is { })
{
return new Dictionary<string, object>
{
["__op"] = "Batch",
["ops"] = new[] { addition, removal }
};
}
return addition ?? removal;
}
}
21 changes: 19 additions & 2 deletions Parse/Infrastructure/Control/ParseSetOperation.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Parse.Abstractions.Infrastructure;
using Parse.Abstractions.Infrastructure.Control;
using Parse.Infrastructure.Data;
Expand Down Expand Up @@ -39,8 +41,6 @@ public IDictionary<string, object> ConvertToJSON(IServiceHub serviceHub = defaul
throw new ArgumentException($"Unsupported type for encoding: {Value?.GetType()?.FullName}");
}



public IParseFieldOperation MergeWithPrevious(IParseFieldOperation previous)
{
// Set operation always overrides previous operations
Expand All @@ -52,6 +52,23 @@ public object Apply(object oldValue, string key)
// Set operation always sets the field to the specified value
return Value;
}
public object ConvertValueToJSON(IServiceHub serviceHub = null)
{
// Get the values of the dictionary
var vals = ConvertToJSON(serviceHub).Values;



// Check if vals is a ValueCollection and contains exactly one element , that's how we get operations working! because they are dict<string,obj> of dict<string,obj>
if (vals.Count == 1)
{
// Return the first and only value
return vals.FirstOrDefault();
}

// Return vals if no single value is found
return vals;
}

public object Value { get; private set; }
}
Loading
Loading