Files
2026-07-13 12:49:17 +08:00

137 lines
5.2 KiB
C#

using System.Text;
using System.Threading;
using MCPForUnity.Editor.Services.AssetGen.Http;
using MCPForUnity.Editor.Services.AssetGen.Providers;
using NUnit.Framework;
namespace MCPForUnityTests.Editor.AssetGen
{
/// <summary>
/// Exercises the Tripo adapter against a <see cref="FakeHttpTransport"/> — no network.
/// Asserts request shaping (endpoint, method, auth scheme, body) and poll-state mapping.
/// The bearer secret is never asserted beyond the "Bearer " scheme prefix.
/// </summary>
public class TripoAdapterTests
{
private static HttpResult Json(string json, int status = 200)
=> new HttpResult
{
Status = status,
IsSuccess = status >= 200 && status < 300,
Text = json,
Body = Encoding.UTF8.GetBytes(json)
};
[Test]
public void Submit_PostsTaskEndpoint_WithBearerHeader_AndReturnsTaskId()
{
var http = new FakeHttpTransport
{
Handler = _ => Json("{\"code\":0,\"data\":{\"task_id\":\"abc123\"}}")
};
var adapter = new TripoAdapter();
var req = new ModelGenRequest { Provider = "tripo", Mode = "text", Prompt = "a red dragon" };
string taskId = adapter.SubmitAsync(req, "tsk_secret_value", http, CancellationToken.None)
.GetAwaiter().GetResult();
Assert.AreEqual("abc123", taskId);
Assert.AreEqual(1, http.RecordedRequests.Count);
HttpRequestSpec rec = http.RecordedRequests[0];
StringAssert.Contains("/v2/openapi/task", rec.Url);
Assert.AreEqual("POST", rec.Method);
Assert.IsTrue(rec.Headers.ContainsKey("Authorization"));
StringAssert.StartsWith("Bearer ", rec.Headers["Authorization"]);
string body = Encoding.UTF8.GetString(rec.Body);
StringAssert.Contains("a red dragon", body);
StringAssert.Contains("text_to_model", body);
}
[Test]
public void Poll_MapsSuccess_WithDownloadUrl()
{
var http = new FakeHttpTransport
{
Handler = _ => Json(
"{\"code\":0,\"data\":{\"status\":\"success\",\"progress\":100," +
"\"output\":{\"pbr_model\":\"https://cdn.tripo3d.ai/model.glb\"}}}")
};
var adapter = new TripoAdapter();
ProviderPollResult res = adapter.PollAsync("abc123", "tsk_secret_value", http, CancellationToken.None)
.GetAwaiter().GetResult();
Assert.AreEqual(ProviderPollState.Succeeded, res.State);
Assert.AreEqual("https://cdn.tripo3d.ai/model.glb", res.DownloadUrl);
}
[Test]
public void Poll_MapsRunning_WithProgress()
{
var http = new FakeHttpTransport
{
Handler = _ => Json("{\"code\":0,\"data\":{\"status\":\"running\",\"progress\":42}}")
};
var adapter = new TripoAdapter();
ProviderPollResult res = adapter.PollAsync("abc123", "k", http, CancellationToken.None)
.GetAwaiter().GetResult();
Assert.AreEqual(ProviderPollState.Running, res.State);
Assert.AreEqual(0.42f, res.Progress, 0.001f);
}
[Test]
public void Poll_MapsFailed_WithError()
{
var http = new FakeHttpTransport
{
Handler = _ => Json("{\"code\":0,\"data\":{\"status\":\"failed\",\"progress\":0}}")
};
var adapter = new TripoAdapter();
ProviderPollResult res = adapter.PollAsync("abc123", "k", http, CancellationToken.None)
.GetAwaiter().GetResult();
Assert.AreEqual(ProviderPollState.Failed, res.State);
Assert.IsNotEmpty(res.Error);
}
[Test]
public void Submit_ImageMode_UsesImageToModel()
{
var http = new FakeHttpTransport
{
Handler = _ => Json("{\"code\":0,\"data\":{\"task_id\":\"img1\"}}")
};
var adapter = new TripoAdapter();
var req = new ModelGenRequest
{
Provider = "tripo",
Mode = "image",
ImageUrl = "https://example.com/in.png"
};
adapter.SubmitAsync(req, "k", http, CancellationToken.None).GetAwaiter().GetResult();
string body = Encoding.UTF8.GetString(http.RecordedRequests[0].Body);
StringAssert.Contains("image_to_model", body);
StringAssert.Contains("https://example.com/in.png", body);
}
[Test]
public void Submit_ImageMode_LocalPathOnly_Throws()
{
// Tripo can't take a local image inline (no data-URI support, upload not wired) — it must
// fail clearly rather than silently falling back to text mode.
var adapter = new TripoAdapter();
var req = new ModelGenRequest { Provider = "tripo", Mode = "image", ImagePath = "/tmp/whatever.png" };
Assert.Throws<System.Exception>(() =>
adapter.SubmitAsync(req, "k", new FakeHttpTransport(), CancellationToken.None).GetAwaiter().GetResult());
}
}
}