Add questionnaire upload flow test

This commit is contained in:
wuwenbo 2026-06-29 16:24:02 +08:00
parent ee49cab50c
commit bccfef69d6

View File

@ -8,6 +8,7 @@
using System;
using System.Text;
using Logic.CrashSight;
using Logic.Multilingual;
using Steamworks;
using TH1_Logic.Core;
@ -208,6 +209,9 @@ namespace Logic.Editor
private string _multilingualSelectedText = "测试多语言选中文本";
private string _multilingualDescription = "测试多语言问题自述";
private MultilingualType _multilingualLanguage = MultilingualType.EN;
private string _questionnaireId = "upload-flow-test";
private string _questionnaireSingleChoiceId = "test-choice-a";
private string _questionnaireOpenText = "测试问卷开放题回答";
private string _status = "就绪";
private string _lastObjectKey = "";
@ -304,6 +308,10 @@ namespace Logic.Editor
_multilingualSelectedText = EditorGUILayout.TextArea(_multilingualSelectedText, GUILayout.MinHeight(55));
EditorGUILayout.LabelField("多语言玩家自述");
_multilingualDescription = EditorGUILayout.TextArea(_multilingualDescription, GUILayout.MinHeight(55));
_questionnaireId = EditorGUILayout.TextField("问卷 ID", _questionnaireId);
_questionnaireSingleChoiceId = EditorGUILayout.TextField("问卷单选选项 ID", _questionnaireSingleChoiceId);
EditorGUILayout.LabelField("问卷开放题回答");
_questionnaireOpenText = EditorGUILayout.TextArea(_questionnaireOpenText, GUILayout.MinHeight(45));
EditorGUILayout.EndVertical();
}
@ -323,7 +331,9 @@ namespace Logic.Editor
TestBugReportUpload();
if (GUILayout.Button("4. 多语言汇报上传 type=multilingualreport", GUILayout.Height(28)))
TestMultilingualReportUpload();
if (GUILayout.Button("顺序测试 1-4", GUILayout.Height(32)))
if (GUILayout.Button("5. 问卷上传 type=questionnaire", GUILayout.Height(28)))
TestQuestionnaireUpload();
if (GUILayout.Button("顺序测试 1-5", GUILayout.Height(32)))
TestAll();
}
@ -412,9 +422,29 @@ namespace Logic.Editor
});
}
private async void TestQuestionnaireUpload()
{
await RunTest("问卷上传", async () =>
{
var steamId = GetSteamId();
var authTicket = GetAuthTicket();
var questionnaireBytes = BuildQuestionnairePayloadBytes(steamId);
var credentials = await _stsService.RequestStsTokenAsync(steamId, authTicket, "questionnaire",
_version);
if (!IsQuestionnaireObjectKey(credentials?.objectKey))
throw new Exception($"服务端返回了非问卷 objectKey: {credentials?.objectKey}");
var success = await _uploadService.UploadFileAsync(credentials, questionnaireBytes,
"application/json", MaxQuestionnaireUploadBytes);
_lastObjectKey = credentials.objectKey ?? "";
return success
? $"问卷上传成功: {credentials.objectKey}"
: $"问卷上传失败: {credentials.objectKey}";
});
}
private async void TestAll()
{
await RunTest("顺序测试 1-4", async () =>
await RunTest("顺序测试 1-5", async () =>
{
var steamId = GetSteamId();
var authTicket = GetAuthTicket();
@ -438,8 +468,16 @@ namespace Logic.Editor
var multilingualOk = await OssManager.Instance.UploadPlayerMultilingualReportAsync(steamId,
multilingualPackage.Data, multilingualPackage.Manifest.version);
_lastObjectKey = multilingualOk.objectKey ?? "";
return $"顺序测试完成: steamauth cached={warmup.cached}; ossdata={(normalOk ? "" : "")} {credentials.objectKey}; bugreport={(bugOk.success ? "" : "")} {bugOk.objectKey}; multilingualreport={(multilingualOk.success ? "" : "")} {multilingualOk.objectKey}";
var questionnaireBytes = BuildQuestionnairePayloadBytes(steamId);
var questionnaireCredentials = await _stsService.RequestStsTokenAsync(steamId, authTicket,
"questionnaire", _version);
if (!IsQuestionnaireObjectKey(questionnaireCredentials?.objectKey))
throw new Exception($"服务端返回了非问卷 objectKey: {questionnaireCredentials?.objectKey}");
var questionnaireOk = await _uploadService.UploadFileAsync(questionnaireCredentials,
questionnaireBytes, "application/json", MaxQuestionnaireUploadBytes);
_lastObjectKey = questionnaireCredentials.objectKey ?? "";
return $"顺序测试完成: steamauth cached={warmup.cached}; ossdata={(normalOk ? "" : "")} {credentials.objectKey}; bugreport={(bugOk.success ? "" : "")} {bugOk.objectKey}; multilingualreport={(multilingualOk.success ? "" : "")} {multilingualOk.objectKey}; questionnaire={(questionnaireOk ? "" : "")} {questionnaireCredentials.objectKey}";
});
}
@ -511,5 +549,145 @@ namespace Logic.Editor
return "(Steam 未初始化)";
}
}
private const int MaxQuestionnaireUploadBytes = 512 * 1024;
private static bool IsQuestionnaireObjectKey(string objectKey)
{
return !string.IsNullOrEmpty(objectKey)
&& objectKey.StartsWith("questionnaire/", StringComparison.Ordinal)
&& objectKey.EndsWith(".json", StringComparison.OrdinalIgnoreCase);
}
[Serializable]
private class QuestionnaireTestPayload
{
public string schema = "th1.questionnaire-answer.v1";
public string responseId;
public string questionnaireId;
public long submittedAtUnix;
public string submittedAtUtc;
public string createdAtUtc;
public string createdAtLocal;
public string timezone;
public string steamId;
public string version;
public string unityVersion;
public string platform;
public string crashSightDeviceId;
public string deviceModel;
public string deviceName;
public string operatingSystem;
public string processorType;
public int processorCount;
public int systemMemorySizeMb;
public string graphicsDeviceName;
public int graphicsMemorySizeMb;
public QuestionnaireTestAnswerSheet answerSheet;
}
[Serializable]
private class QuestionnaireTestAnswerSheet
{
public string QuestionnaireId;
public long SubmittedAtUnix;
public string SubmittedAtUtc;
public string LastUploadObjectKey = "";
public string LastUploadedAtUtc = "";
public string LastUploadError = "";
public QuestionnaireTestAnswer[] Answers;
}
[Serializable]
private class QuestionnaireTestAnswer
{
public string QuestionId;
public int QuestionType;
public string[] SelectedOptionIds;
public string OpenText;
}
private byte[] BuildQuestionnairePayloadBytes(string steamId)
{
var now = DateTimeOffset.UtcNow;
var questionnaireId = string.IsNullOrWhiteSpace(_questionnaireId)
? "upload-flow-test"
: _questionnaireId.Trim();
var selectedOptionId = string.IsNullOrWhiteSpace(_questionnaireSingleChoiceId)
? "test-choice-a"
: _questionnaireSingleChoiceId.Trim();
var openText = string.IsNullOrWhiteSpace(_questionnaireOpenText)
? "测试问卷开放题回答"
: _questionnaireOpenText.Trim();
var submittedAtUtc = now.UtcDateTime.ToString("O");
var answerSheet = new QuestionnaireTestAnswerSheet
{
QuestionnaireId = questionnaireId,
SubmittedAtUnix = now.ToUnixTimeSeconds(),
SubmittedAtUtc = submittedAtUtc,
Answers = new[]
{
new QuestionnaireTestAnswer
{
QuestionId = "upload-flow-single-choice",
QuestionType = 1,
SelectedOptionIds = new[] { selectedOptionId },
OpenText = ""
},
new QuestionnaireTestAnswer
{
QuestionId = "upload-flow-open",
QuestionType = 0,
SelectedOptionIds = Array.Empty<string>(),
OpenText = openText
}
}
};
var payload = new QuestionnaireTestPayload
{
responseId = Guid.NewGuid().ToString("N"),
questionnaireId = questionnaireId,
submittedAtUnix = answerSheet.SubmittedAtUnix,
submittedAtUtc = submittedAtUtc,
createdAtUtc = DateTime.UtcNow.ToString("O"),
createdAtLocal = DateTime.Now.ToString("O"),
timezone = GetLocalTimezone(),
steamId = steamId ?? "",
version = string.IsNullOrWhiteSpace(_version) ? PlayerBugReportService.GetCurrentVersion() : _version.Trim(),
unityVersion = Application.unityVersion,
platform = Application.platform.ToString(),
crashSightDeviceId = CrashSightManager.GetCrashSightDeviceId(),
deviceModel = SystemInfo.deviceModel,
deviceName = SystemInfo.deviceName,
operatingSystem = SystemInfo.operatingSystem,
processorType = SystemInfo.processorType,
processorCount = SystemInfo.processorCount,
systemMemorySizeMb = SystemInfo.systemMemorySize,
graphicsDeviceName = SystemInfo.graphicsDeviceName,
graphicsMemorySizeMb = SystemInfo.graphicsMemorySize,
answerSheet = answerSheet
};
var bytes = new UTF8Encoding(false).GetBytes(JsonUtility.ToJson(payload, true));
if (bytes.Length > MaxQuestionnaireUploadBytes)
throw new Exception($"问卷测试 payload 大小 {bytes.Length} 超过 {MaxQuestionnaireUploadBytes} 字节限制");
return bytes;
}
private static string GetLocalTimezone()
{
try
{
var offset = DateTimeOffset.Now.Offset;
var sign = offset < TimeSpan.Zero ? "-" : "+";
return $"{TimeZoneInfo.Local.Id} (UTC{sign}{offset.Duration():hh\\:mm})";
}
catch
{
return DateTimeOffset.Now.Offset.ToString();
}
}
}
}