mirror of
https://github.com/Shu-Ying/farm_service.git
synced 2026-11-13 00:30:03 +08:00
add # 新增字体
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"hooks": {
|
||||||
|
"PreToolUse": [
|
||||||
|
{
|
||||||
|
"matcher": "Write|Edit",
|
||||||
|
"hooks": [
|
||||||
|
{
|
||||||
|
"type": "command",
|
||||||
|
"command": "node \"D:/nonebot/farm_service/.agents/skills/comet/scripts/comet-hook-router.mjs\" --platform \"codex\" --project-root \"D:/nonebot/farm_service\""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
# Comet 使用的配置格式版本,请勿修改此值。
|
||||||
|
schema: comet.project.v1
|
||||||
|
# `/comet` 默认进入的工作流;该值也必须出现在 workflows 中。
|
||||||
|
default_workflow: native
|
||||||
|
# 此项目启用的工作流,可填写 native、classic 或同时启用两者。
|
||||||
|
workflows:
|
||||||
|
- native
|
||||||
|
# 是否启用只读的环境感知恢复探针,同时作用于 Native 和 Classic;设为 false 可关闭自动工作流恢复。
|
||||||
|
# ambient_resume: true | false
|
||||||
|
ambient_resume: true
|
||||||
|
# 当前项目的个人记忆自动学习与注入策略。
|
||||||
|
memory:
|
||||||
|
# 是否允许当前项目通过工作流事件自动沉淀新的个人记忆。
|
||||||
|
# 可选值:true | false
|
||||||
|
learning: true
|
||||||
|
# 是否允许当前项目把个人记忆自动注入 Agent 上下文。
|
||||||
|
# 可选值:true | false
|
||||||
|
retrieval: true
|
||||||
|
# 普通 Comet 任务使用的项目知识检索 Provider。
|
||||||
|
knowledge:
|
||||||
|
# 项目知识 Provider。
|
||||||
|
# 可选值:local | remote
|
||||||
|
provider: local
|
||||||
|
# Native 工作流配置,不会改变 Classic 的状态或行为。
|
||||||
|
native:
|
||||||
|
# Native 规格和 change 的存放根目录;运行时数据始终位于 .comet。
|
||||||
|
artifact_root: docs
|
||||||
|
# Native 工作流文档使用的产物语言。
|
||||||
|
# 可选值:en | zh-CN
|
||||||
|
language: zh-CN
|
||||||
|
# Native 提问澄清问题的方式:batch 每轮一次提出当前所有可回答的问题(默认),sequential 每轮只问一个。
|
||||||
|
# 可选值:batch | sequential
|
||||||
|
clarification_mode: batch
|
||||||
|
# Native 归档检查成功后自动归档,或等待用户明确确认。
|
||||||
|
# 可选值:automatic | required
|
||||||
|
archive_confirmation: automatic
|
||||||
|
# 同一个已确认验收目标最多允许的 Verify 失败次数;达到上限后停止完成循环。
|
||||||
|
max_verify_failures: 5
|
||||||
@@ -8,3 +8,5 @@ RUST_LOG=farm_service=info
|
|||||||
# Optional: leave unset to disable asset distribution. Sibling assets.zip required.
|
# Optional: leave unset to disable asset distribution. Sibling assets.zip required.
|
||||||
# The release files live in this repository's root assets/ directory.
|
# The release files live in this repository's root assets/ directory.
|
||||||
# FARM_ASSET_MANIFEST=./assets/manifest.json
|
# FARM_ASSET_MANIFEST=./assets/manifest.json
|
||||||
|
# Optional: independent Xiaolai font archive, including the complete OFL license.
|
||||||
|
# FARM_FONT_MANIFEST=./fonts/manifest.json
|
||||||
|
|||||||
@@ -36,3 +36,8 @@ profraw
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
Desktop.ini
|
Desktop.ini
|
||||||
|
# >>> Comet managed project state >>>
|
||||||
|
!/.comet/
|
||||||
|
/.comet/*
|
||||||
|
!/.comet/config.yaml
|
||||||
|
# <<< Comet managed project state <<<
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
- `GET /health`:公开就绪检查,实际检查数据库。
|
- `GET /health`:公开就绪检查,实际检查数据库。
|
||||||
- `GET /v1/assets/manifest.json`:公开素材清单,含版本、SHA256、大小及三源绝对URL。
|
- `GET /v1/assets/manifest.json`:公开素材清单,含版本、SHA256、大小及三源绝对URL。
|
||||||
- `GET /v1/assets/{sha256}/assets.zip`:下载启动时校验过的素材快照,支持ETag。
|
- `GET /v1/assets/{sha256}/assets.zip`:下载启动时校验过的素材快照,支持ETag。
|
||||||
|
- `GET /v1/fonts/manifest.json`:独立字体资源清单。
|
||||||
|
- `GET /v1/fonts/{sha256}/assets.zip`:原版小赖字体与完整 OFL 许可,支持 ETag。
|
||||||
- [资源分发接口与三端接入约定](docs/assets.md)。
|
- [资源分发接口与三端接入约定](docs/assets.md)。
|
||||||
- 素材发布文件位于根目录 `assets/manifest.json` 和 `assets/assets.zip`;
|
- 素材发布文件位于根目录 `assets/manifest.json` 和 `assets/assets.zip`;
|
||||||
ZIP 仅由本服务仓库发布,三个插件分发包不包含 ZIP。
|
ZIP 仅由本服务仓库发布,三个插件分发包不包含 ZIP。
|
||||||
@@ -65,6 +67,7 @@ cargo run --locked
|
|||||||
| `FARM_LOG_DIR` | `./logs`,保存每日滚动日志 |
|
| `FARM_LOG_DIR` | `./logs`,保存每日滚动日志 |
|
||||||
| `RUST_LOG` | `farm_service=info` |
|
| `RUST_LOG` | `farm_service=info` |
|
||||||
| `FARM_ASSET_MANIFEST` | 可选,建议 `./assets/manifest.json`;同目录须有assets.zip;未设置时资源接口返回503 |
|
| `FARM_ASSET_MANIFEST` | 可选,建议 `./assets/manifest.json`;同目录须有assets.zip;未设置时资源接口返回503 |
|
||||||
|
| `FARM_FONT_MANIFEST` | 可选,设为 `./fonts/manifest.json`;同目录须有 assets.zip;未设置时字体接口返回503 |
|
||||||
|
|
||||||
相对目录以进程工作目录为基准;生产建议使用固定绝对路径。
|
相对目录以进程工作目录为基准;生产建议使用固定绝对路径。
|
||||||
`.env.example` 只是模板,程序**不会自动加载 .env**。
|
`.env.example` 只是模板,程序**不会自动加载 .env**。
|
||||||
@@ -133,6 +136,13 @@ cargo clippy --locked --all-targets -- -D warnings
|
|||||||
|
|
||||||
## 开源协议
|
## 开源协议
|
||||||
|
|
||||||
|
字体资源独立于服务代码许可。`fonts/Xiaolai-Regular.ttf` 为未经修改的
|
||||||
|
小赖字体 v3.126,采用 SIL OFL 1.1;完整版权与许可见
|
||||||
|
[OFL-Xiaolai.txt](fonts/OFL-Xiaolai.txt)。`fonts/assets.zip` 同时包含字体
|
||||||
|
和许可,不得单独出售字体文件。字体包通过 GitHub/Gitea 的本仓库
|
||||||
|
`fonts/assets.zip` raw 地址或上述服务接口分发,插件缓存时校验摘要。
|
||||||
|
资源迁移仅准备本地文件,须发布此仓库或部署服务后远端才能提供新版资源。
|
||||||
|
|
||||||
Copyright (c) 2026 Shu-Ying。
|
Copyright (c) 2026 Shu-Ying。
|
||||||
本项目采用 GNU Affero General Public License v3.0,
|
本项目采用 GNU Affero General Public License v3.0,
|
||||||
SPDX 标识为 `AGPL-3.0-only`,完整条款见 [LICENSE](LICENSE)。
|
SPDX 标识为 `AGPL-3.0-only`,完整条款见 [LICENSE](LICENSE)。
|
||||||
@@ -142,3 +152,7 @@ SPDX 标识为 `AGPL-3.0-only`,完整条款见 [LICENSE](LICENSE)。
|
|||||||
显著提供免费取得该修改版本对应源码的机会。
|
显著提供免费取得该修改版本对应源码的机会。
|
||||||
源码地址应指向实际运行版本,不能仅指向不包含这些修改的上游仓库。
|
源码地址应指向实际运行版本,不能仅指向不包含这些修改的上游仓库。
|
||||||
协议针对程序及其对应源码,不要求公开 API 令牌、用户资料或数据库内容。
|
协议针对程序及其对应源码,不要求公开 API 令牌、用户资料或数据库内容。
|
||||||
|
|
||||||
|
## 致谢
|
||||||
|
|
||||||
|
- [小赖字体](https://github.com/lxgw/kose-font) *lxgw 提供的开源中文手写字体,采用 SIL OFL 1.1,分发时附带完整字体许可*
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
Copyright 2020-2024 LXGW (https://github.com/lxgw/kose-font)
|
||||||
|
Copyright 2014 Nozomi Seto (https://ja.osdn.net/projects/setofont/)
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"schema": 1,
|
||||||
|
"version": "xiaolai-v3.126",
|
||||||
|
"sha256": "c2d1c1a5247e05b766d12a8d8410594d78b1a8b86031624a05b76ee0515c1335",
|
||||||
|
"size_bytes": 14741741,
|
||||||
|
"sources": {
|
||||||
|
"github": "https://raw.githubusercontent.com/Shu-Ying/farm_service/main/fonts/assets.zip",
|
||||||
|
"gitea": "https://gitea.diuse.work/Diuse/farm_service/raw/branch/main/fonts/assets.zip",
|
||||||
|
"farm_service": "https://farm.example/v1/fonts/c2d1c1a5247e05b766d12a8d8410594d78b1a8b86031624a05b76ee0515c1335/assets.zip"
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
-2
@@ -51,6 +51,10 @@ pub struct Manifest {
|
|||||||
|
|
||||||
impl Manifest {
|
impl Manifest {
|
||||||
pub fn validate(&self) -> Result<()> {
|
pub fn validate(&self) -> Result<()> {
|
||||||
|
self.validate_for("assets")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn validate_for(&self, namespace: &str) -> Result<()> {
|
||||||
ensure!(self.schema == 1, "unsupported asset manifest schema");
|
ensure!(self.schema == 1, "unsupported asset manifest schema");
|
||||||
ensure!(
|
ensure!(
|
||||||
!self.version.is_empty()
|
!self.version.is_empty()
|
||||||
@@ -84,7 +88,7 @@ impl Manifest {
|
|||||||
ensure!(
|
ensure!(
|
||||||
service_url
|
service_url
|
||||||
.path()
|
.path()
|
||||||
.ends_with(&format!("/v1/assets/{}/assets.zip", self.sha256)),
|
.ends_with(&format!("/v1/{namespace}/{}/assets.zip", self.sha256)),
|
||||||
"farm_service URL must include the bundle SHA256"
|
"farm_service URL must include the bundle SHA256"
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -128,10 +132,19 @@ pub struct AssetBundle {
|
|||||||
impl AssetBundle {
|
impl AssetBundle {
|
||||||
/// Load manifest.json and its sibling assets.zip, verifying both at startup.
|
/// Load manifest.json and its sibling assets.zip, verifying both at startup.
|
||||||
pub fn load(manifest_path: &Path) -> Result<Self> {
|
pub fn load(manifest_path: &Path) -> Result<Self> {
|
||||||
|
Self::load_for(manifest_path, "assets")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load an independent font archive with the same integrity contract.
|
||||||
|
pub fn load_font(manifest_path: &Path) -> Result<Self> {
|
||||||
|
Self::load_for(manifest_path, "fonts")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_for(manifest_path: &Path, namespace: &str) -> Result<Self> {
|
||||||
let manifest: Manifest =
|
let manifest: Manifest =
|
||||||
serde_json::from_slice(&read_bounded(manifest_path, MAX_MANIFEST_BYTES)?)
|
serde_json::from_slice(&read_bounded(manifest_path, MAX_MANIFEST_BYTES)?)
|
||||||
.context("invalid asset manifest JSON")?;
|
.context("invalid asset manifest JSON")?;
|
||||||
manifest.validate()?;
|
manifest.validate_for(namespace)?;
|
||||||
let content = read_bounded(
|
let content = read_bounded(
|
||||||
&manifest_path.with_file_name("assets.zip"),
|
&manifest_path.with_file_name("assets.zip"),
|
||||||
MAX_BUNDLE_BYTES,
|
MAX_BUNDLE_BYTES,
|
||||||
@@ -169,6 +182,14 @@ pub fn router(bundle: Option<AssetBundle>) -> Router {
|
|||||||
.with_state(bundle)
|
.with_state(bundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Font distribution is independent of the picture bundle and profile APIs.
|
||||||
|
pub fn font_router(bundle: Option<AssetBundle>) -> Router {
|
||||||
|
Router::new()
|
||||||
|
.route("/v1/fonts/manifest.json", get(get_manifest))
|
||||||
|
.route("/v1/fonts/{sha256}/assets.zip", get(download))
|
||||||
|
.with_state(bundle)
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_manifest(State(bundle): State<Option<AssetBundle>>) -> Response {
|
async fn get_manifest(State(bundle): State<Option<AssetBundle>>) -> Response {
|
||||||
match bundle {
|
match bundle {
|
||||||
Some(bundle) => {
|
Some(bundle) => {
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ pub struct Config {
|
|||||||
pub log_dir: PathBuf,
|
pub log_dir: PathBuf,
|
||||||
/// Optional verified asset manifest; sibling assets.zip is loaded once.
|
/// Optional verified asset manifest; sibling assets.zip is loaded once.
|
||||||
pub asset_manifest: Option<PathBuf>,
|
pub asset_manifest: Option<PathBuf>,
|
||||||
|
/// Optional font archive manifest; includes the original OFL license.
|
||||||
|
pub font_manifest: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@@ -33,6 +35,7 @@ impl Config {
|
|||||||
.context("invalid FARM_BIND address")?,
|
.context("invalid FARM_BIND address")?,
|
||||||
api_token,
|
api_token,
|
||||||
asset_manifest: env::var_os("FARM_ASSET_MANIFEST").map(PathBuf::from),
|
asset_manifest: env::var_os("FARM_ASSET_MANIFEST").map(PathBuf::from),
|
||||||
|
font_manifest: env::var_os("FARM_FONT_MANIFEST").map(PathBuf::from),
|
||||||
data_dir: env::var_os("FARM_DATA_DIR")
|
data_dir: env::var_os("FARM_DATA_DIR")
|
||||||
.map(PathBuf::from)
|
.map(PathBuf::from)
|
||||||
.unwrap_or_else(|| PathBuf::from("data")),
|
.unwrap_or_else(|| PathBuf::from("data")),
|
||||||
|
|||||||
+9
-1
@@ -24,13 +24,21 @@ async fn main() -> Result<()> {
|
|||||||
.map(assets::AssetBundle::load)
|
.map(assets::AssetBundle::load)
|
||||||
.transpose()
|
.transpose()
|
||||||
.context("asset startup verification failed")?;
|
.context("asset startup verification failed")?;
|
||||||
|
let fonts = config
|
||||||
|
.font_manifest
|
||||||
|
.as_deref()
|
||||||
|
.map(assets::AssetBundle::load_font)
|
||||||
|
.transpose()
|
||||||
|
.context("font startup verification failed")?;
|
||||||
std::fs::create_dir_all(&config.data_dir)?;
|
std::fs::create_dir_all(&config.data_dir)?;
|
||||||
let pool = db::open_database(&config.data_dir.join("farm_service.sqlite3"))
|
let pool = db::open_database(&config.data_dir.join("farm_service.sqlite3"))
|
||||||
.await
|
.await
|
||||||
.context("database startup failed")?;
|
.context("database startup failed")?;
|
||||||
let listener = tokio::net::TcpListener::bind(config.bind).await?;
|
let listener = tokio::net::TcpListener::bind(config.bind).await?;
|
||||||
tracing::info!(address = %listener.local_addr()?, "service_ready");
|
tracing::info!(address = %listener.local_addr()?, "service_ready");
|
||||||
let app = api::router(pool.clone(), config.api_token).merge(assets::router(bundle));
|
let app = api::router(pool.clone(), config.api_token)
|
||||||
|
.merge(assets::router(bundle))
|
||||||
|
.merge(assets::font_router(fonts));
|
||||||
let result = axum::serve(listener, app)
|
let result = axum::serve(listener, app)
|
||||||
.with_graceful_shutdown(shutdown())
|
.with_graceful_shutdown(shutdown())
|
||||||
.await;
|
.await;
|
||||||
|
|||||||
@@ -39,6 +39,40 @@ fn save(directory: &tempfile::TempDir, manifest: &Manifest) {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn independent_font_archive_is_verified_and_public() {
|
||||||
|
let (directory, mut manifest, content) = fixture();
|
||||||
|
manifest.sources.farm_service = format!(
|
||||||
|
"https://farm.example/v1/fonts/{}/assets.zip",
|
||||||
|
manifest.sha256
|
||||||
|
);
|
||||||
|
save(&directory, &manifest);
|
||||||
|
let path = directory.path().join("manifest.json");
|
||||||
|
assert!(AssetBundle::load(&path).is_err());
|
||||||
|
let bundle = AssetBundle::load_font(&path).unwrap();
|
||||||
|
let app = assets::font_router(Some(bundle));
|
||||||
|
let uri = format!("/v1/fonts/{}/assets.zip", manifest.sha256);
|
||||||
|
let response = app
|
||||||
|
.clone()
|
||||||
|
.oneshot(Request::builder().uri(uri).body(Body::empty()).unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::OK);
|
||||||
|
assert_eq!(to_bytes(response.into_body(), 4096).await.unwrap(), content);
|
||||||
|
let response = app
|
||||||
|
.oneshot(
|
||||||
|
Request::builder()
|
||||||
|
.uri("/v1/fonts/unknown/assets.zip")
|
||||||
|
.body(Body::empty())
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(response.status(), StatusCode::NOT_FOUND);
|
||||||
|
std::fs::write(directory.path().join("assets.zip"), b"bad").unwrap();
|
||||||
|
assert!(AssetBundle::load_font(&path).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn public_manifest_download_cache_and_snapshot() {
|
async fn public_manifest_download_cache_and_snapshot() {
|
||||||
let (directory, manifest, content) = fixture();
|
let (directory, manifest, content) = fixture();
|
||||||
|
|||||||
Reference in New Issue
Block a user