| English Version | 中文版 |
Status: Draft Version: 0.7 Date: 2026-09-05 Applies-To: NPS-AaaS-Profile §4.3 — Level 2 Standard Authors: Ori Lynn / INNO LOTUS PTY LTD
本文定义部署声明 NPS-AaaS-Profile L2 合规前必须处置的测试用例。它覆盖当前 L2-01..L2-08 服务合同,以及按角色适用的 transport、Bridge 与 HA 用例族。 历史上把 L2-01..L2-07 整体延期是无效的:这些要求已经存在于 alpha.20 之前的 规范 Profile。其 alpha.19 冻结处置见
aaas-l2-requirement-disposition.json。
impl/dotnet/src/NPS.NWP.Anchor/ 下的 AnchorNodeClient,CR-0002 §4)。NPS-NODE-L2-CERTIFIED.md 复制到 IUT 仓库根目录,
填完每个字段,用 IUT 的 root 私钥对声明块签名。paired-peer 方法论与 NPS-Node-L1 §1 一致。peer 要求升级: 凡 L1 要求”已通过 L1 的 peer”之处,L2 改为”已通过 L2 的 peer”。
本次发布接受自声明。第三方认证(NPS Cloud CA)规划于 2027 Q1+ 的 L3 阶段,不在本套件范围。
| 项 | 值 |
|---|---|
| 网络 | 仅 loopback;用例不要求外部 DNS 或跨机路由 |
| Peer | 任一 L2 合规实现(推荐:NPS.NWP.Anchor + NPS.NDP NPS v1.0.0-alpha.11 或更高) |
| 时钟 | 与 peer 墙钟偏差 ≤ ±5 秒(ISO 8601 UTC) |
| 文件系统 | IUT 密钥库与(如适用)拓扑持久化可写目录 |
| Wire 编码 | Tier-1 JSON MUST 覆盖;Tier-2 MsgPack SHOULD 覆盖 |
| 集群前置 | IUT MUST 充当单集群的 Anchor Node;成员节点由 peer 模拟 —— 通过 NDP Announce 帧携带 cluster_anchor = IUT 的 NID |
| 多 Anchor 前置 | 仅 §3.4(TC-N2-HA-*)需要:peer 额外模拟同一 cluster_anchor 集群下的一个或多个 standby Anchor;TC-N2-HA-07 / -08 中 IUT 以 NDP Registry 角色而非 Anchor 角色受测 |
每个用例从 IUT 干净状态开始:用例间 MUST 清空 IUT 拓扑注册表,除非用例显式标注为 continuation。
每个用例列出 NPS-AaaS-Profile §4.3 的 Req ID、前置条件、操作 步骤、验收条件。
topology.snapshot / topology.stream本组用例验证 L2-08:实现 NPS-2 §12 定义的保留查询类型。 TC-N2-AnchorTopo-01 至 TC-N2-AnchorStream-04 覆盖正向路径;TC-N2-AnchorTopo-04 至 TC-N2-AnchorTopo-08 覆盖必要的负路径(每个错误码对应一个 MUST 拒绝场景)。L2-08 需全部十二个用例通过方可声明合规。
Req:L2-08(topology.snapshot)
前置:IUT 充当 Anchor,无既有拓扑状态。
操作:
Announce 帧(cluster_anchor = IUT NID)声明三个成员节点(M1 Memory、M2 Action、M3 Complex)。QueryFrame,type = "topology.snapshot"、topology.scope = "cluster"、topology.include 取默认。
通过条件:
CapsFrame 应答,anchor_ref = "nps:system:topology:snapshot"。cluster_size = 3。members 数组恰好包含 M1、M2、M3 三个 NID(顺序不限)。nid、node_roles、activation_mode。version 是正整数。anchor_nid 等于 IUT NID。Req:L2-08(version 语义,NPS-2 §12.3) 前置:IUT 充当 Anchor,无既有拓扑状态。 操作:
M1。snapshot_a(记下 version = V1)。M2。snapshot_b(记下 version = V2)。
通过条件:
V2 > V1 严格成立。snapshot_b.cluster_size = snapshot_a.cluster_size + 1。version 计数器在本次运行内全程单调。child_anchor + member_countReq:L2-08(子 Anchor 成员表示,NPS-2 §12.1)
前置:IUT 充当 Anchor;peer 模拟一个本身有 2 个成员的子 Anchor CA。
操作:
CA:node_roles = ["anchor"],cluster_anchor = IUT NID,元数据指明 CA 自身有 2 个成员。topology.depth = 1 抓 IUT 的快照。
通过条件:
CA 对应的 member 对象 child_anchor: true。CA 对应的 member 对象 member_count: 2。truncated 字段缺省或为 false(深度 1 是默认值,未触及上限)。member_joinedReq:L2-08(topology.stream 加入事件,NPS-2 §12.2)
前置:IUT 充当 Anchor,无既有拓扑状态。
操作:
SubscribeFrame 订阅,type = "topology.stream"、topology.scope = "cluster"。subscribed 应答。M1。
通过条件:
DiffFrame,event_type = "member_joined"。payload 是完整 member 对象,nid 与 M1 匹配。seq 大于 subscribed 应答中返回的 last_seq。member_leftReq:L2-08(topology.stream 离开事件,NPS-2 §12.2)
前置:IUT 充当 Anchor,已声明一个 TTL 较短(如 2 秒)的成员 M1。
操作:
topology.stream。M1 的 announce。TTL + 1 秒 内,IUT 推送 DiffFrame,event_type = "member_left"。payload.nid 与 M1 的 NID 匹配。seq 严格大于上一次 member_joined 事件的 seq。topology.since_version 恢复重播Req:L2-08(重播语义,NPS-2 §12.2 / §12.3) 前置:IUT 充当 Anchor,无既有拓扑状态。 操作:
topology.stream,声明 M1。Peer 记录得到的 seq = V1。M2、M3(得到 V2、V3)。topology.since_version = V1。
通过条件:
M2 与 M3 的 member_joined,顺序为 V2、V3。M1(即 V1)不被重播(V1 是边界,重播严格在其之后)。resync_required 事件。topology:read)→ NWP-TOPOLOGY-UNAUTHORIZEDReq: L2-08(鉴权门控,NPS-2 §12.4;M6) Fixture:IUT 作为 Anchor,已有一个已公告成员。 Action:
capabilities 中不含 topology:read(其他必要能力均已包含)。type = "topology.snapshot" 的 QueryFrame。
Pass:
NWP-TOPOLOGY-UNAUTHORIZED 的 ErrorFrame。NWP-TOPOLOGY-DEPTH-UNSUPPORTEDReq: L2-08(depth 限制执行,NPS-2 §12.1)
Fixture:IUT 作为 Anchor,最大 topology.depth 可通过配置指定;未另行指定时使用 max_depth = 3。
Action:
topology:read)发送 type = "topology.snapshot" 且 topology.depth = max_depth + 1(例如 max_depth = 3 时请求 4)的 QueryFrame。
Pass:
NWP-TOPOLOGY-DEPTH-UNSUPPORTED 的 ErrorFrame。topology.scope 值 → NWP-TOPOLOGY-UNSUPPORTED-SCOPEReq: L2-08(scope 校验,NPS-2 §12.1) Fixture:IUT 作为 Anchor,已有一个已公告成员。 Action:
topology:read)发送 type = "topology.snapshot" 且 topology.scope = "nonexistent_scope" 的 QueryFrame。
Pass:
NWP-TOPOLOGY-UNSUPPORTED-SCOPE 的 ErrorFrame。topology.filter 键 → NWP-TOPOLOGY-FILTER-UNSUPPORTEDReq: L2-08(filter 键校验,NPS-2 §12.1) Fixture:IUT 作为 Anchor,已有一个已公告成员。 Action:
topology:read)发送 type = "topology.snapshot" 且 topology.filter = { "nonexistent_key": "value" } 的 QueryFrame。
Pass:
NWP-TOPOLOGY-FILTER-UNSUPPORTED 的 ErrorFrame。type 值 → NWP-RESERVED-TYPE-UNSUPPORTEDReq: L2-08(保留类型校验,NPS-2 §12;M4) Fixture:IUT 作为 Anchor。 Action:
topology:read)发送 type = "topology.nonexistent_operation" 的 QueryFrame。
Pass:
NWP-RESERVED-TYPE-UNSUPPORTED 的 ErrorFrame。NWP-ACTION-NOT-FOUND(这两个错误码含义明确不同——见 NPS-2 §13)。resync_requiredReq:L2-08(resync_required 语义,NPS-2 §12.2)
前置:IUT 充当 Anchor,保留环形缓冲配置为 5 个事件。
操作:
seq 取值 V1..V10)。topology.stream,带 topology.since_version = 1。
通过条件:
event_type = "resync_required",payload.reason = "version_too_old"。resync_required 事件之前不发送任何成员事件。topology.snapshot 后再无 topology.since_version 订阅来恢复。以下用例适用于在 L2 入站网关(如 nps-ingress)终结原生模式 NCP-over-TLS 的 IUT,
验证 NPS-RFC-0006 §6 与
NCP v0.8 §7.5 新增的传输安全准入门。
nps/1.0Req:NPS-RFC-0006 §6.1–§6.2 前置:IUT 充当 L2 终结器,已配置服务器证书。 操作:
nps/1.0 与有效客户端证书。
通过条件:
nps/1.0。no_application_protocol(120)被拒。Req:NPS-RFC-0006 §6.3(mTLS)
前置:IUT 配置 RequireClientCert = true。
操作:
Req:NPS-RFC-0006 §6.3 前置:IUT 配置了信任锚;Peer 持有链到该信任锚的 NIP 叶证书。 操作:
NCP-NID-MISMATCHReq:NPS-RFC-0006 §6.3,错误码 NCP-NID-MISMATCH
前置:IUT 同上。
操作:
urn:nps:agent:A 的有效证书,发送 preamble + HelloFrame,并等待
IUT 转发后端 CapsFrame。urn:nps:agent:B 的 IdentFrame。
通过条件:
NCP-NID-MISMATCH 关闭会话,且不把该 IdentFrame 转发给后端。以下用例适用于声明了非空 bridge_inbound_protocols(NPS-4 §3.1)的 IUT ——
即主张入站 Bridge 合规 profile 的实现。既有的出站 bridge_target 往返向量
(NPS-2 §16.4)仍是出站 profile 的套件,保持不变;
NPS-CR-0010 §4 把两个族分别称为 TC-N2-BRIDGE-OUT-* / TC-N2-BRIDGE-IN-*,
在本文档中分别落地为 §16.4 向量与 TC-N2-BridgeIn-01..06。
Req:NPS-2 §16.1.2 MUST-3
前置:IUT 声明 bridge_inbound_protocols: ["mcp"],前端一个 Memory Node 与一个 Action Node。
操作:
initialize、ping、
tools/list、tools/call、resources/list、resources/read。
通过条件:
tools/list 以限定名 node__action 暴露 Action Node 的 action(CR-0010 §5.1)。resources/list / resources/read 投影 Memory Node 的读表面;未前端 Memory Node 的 IUT
也 MUST 服务这两个方法(返回空集),不得返回 “method not found”。Req:NPS-2 §16.1.2 MUST-2、MUST-3
前置:IUT 声明 bridge_inbound_protocols: ["grpc"],前端一个 Action Node;客户端使用已发布的 nwp_ingress.proto 契约。
操作:
Req:NPS-2 §16.1.2 MUST-2、MUST-3
前置:IUT 声明 bridge_inbound_protocols: ["a2a"],前端一个 Action Node。
操作:
tasks/send。
通过条件:
tasks/send 完成并把 action 结果作为 task artifact 返回。Req:NPS-CR-0010 §5.1(输入宽容)
前置:IUT 前端两个节点:恰好一个定义 action orders_lookup,两个都定义 action status。
操作:
orders_lookup 调用 tools/call(MCP)。status 调用 tools/call。
通过条件:
Req:NPS-2 §16.1.2 MUST-4、§16.3
前置:IUT 声明 bridge_inbound_protocols: ["mcp"],前端一个可被制造失败的 Action Node。
操作:
isError: true 的成功结果返回。Req:NPS-2 §16.1.2 MUST-5,错误码 NWP-BRIDGE-DIRECTION-UNSUPPORTED
前置:IUT 仅声明 bridge_inbound_protocols: ["mcp"]。
操作:
tasks/send。
通过条件:
NWP-BRIDGE-DIRECTION-UNSUPPORTED 被拒。hint SHOULD 同时携带两个已声明数组(bridge_protocols、bridge_inbound_protocols)。本组用例落地 NPS-CR-0009 §4 承诺的 TC-N2-HA-* 族,
验证该 CR 引入的集群所有权契约:cluster_epoch 围栏(NPS-2 §12.2)、
两个已定稿的 anchor_state 子类型 anchor_failover / anchor_quorum_lost,以及最高 epoch
解析规则(NPS-4 §9)。多 Anchor HA 的运行属于 AaaS Profile L3
要求;L1/L2 的单 Anchor 集群依然完全合规,由 TC-N2-HA-09 覆盖。
该契约横跨两种节点角色,因此 §4 把本族拆成三组认证:TC-N2-HA-01..06 以 Anchor 角色
考察 IUT;TC-N2-HA-07..08 以 NDP Registry 角色考察 IUT;TC-N2-HA-09 是第一组的
单 Anchor 镜像用例,与之互斥。
本小节的 fixture 约定:C 为集群稳定的 cluster_anchor NID;A1 / A2 为该集群的 Anchor;
E 为当前活跃 owner 持有的 cluster_epoch。所有权如何取得、续租、交出属于实现自定义 ——
CR-0009 §1 刻意不规定共识传输 —— 因此每个用例只给出可观测的触发条件,IUT MUST 文档化
用于驱动该触发的控制面。
CR-0009 未定、在此为可测性钉住的两点:(a) 入站帧携带低于接收方自身的
cluster_epoch不在 CR-0009 §3.1 覆盖范围内;本组用例对此不做任何断言,在后续 CR 定论前 实现 MAY 接受或拒绝。(b) CR-0009 §3.3 只说 quorum 恢复由”一个带全新cluster_epoch的 正常anchor_state事件”发出信号,未指明子类型,故 TC-N2-HA-04 只断言 epoch 刷新, 不断言具体的field取值。
cluster_epochReq:NPS-CR-0009 §3.1(所有权围栏)、NPS-2 §12.2
前置:IUT 充当集群 C 的活跃 Anchor,持有 cluster_epoch = E;peer 模拟 standby A2;已公告一个成员。
操作:
topology:read)发送 type = "topology.snapshot" 的 QueryFrame。SubscribeFrame 以 type = "topology.stream" 订阅,并读取 subscribed 应答。
通过条件:
cluster_epoch,类型为 uint64 且 ≥ 1。subscribed 应答报告的 cluster_epoch 与快照一致。C 所有权的 epoch;IUT 不得在两个表面报告不同的 epoch。anchor_failover 线格式Req:NPS-CR-0009 §3.2、NPS-2 §12.2 anchor_state 子类型 anchor_failover
前置:IUT 充当 C 的活跃 Anchor,cluster_epoch = E;peer 模拟的 standby A2 已就绪待接管。
操作:
topology.stream 并等待 subscribed 应答。A2 的优雅所有权交接。
通过条件:
DiffFrame,event_type = "anchor_state"、payload.field = "anchor_failover"。payload.details 携带全部三个必填字段 successor_nid、cluster_epoch、reason。successor_nid 等于 A2 的 NID;cluster_epoch 为 uint64 且严格大于 E;reason = "planned"。member_updated 或 resync_required —— 必须使用带 field 判别符的 anchor_state 信封。anchor_failover 是终结事件Req:NPS-CR-0009 §3.2(被围栏的前任 leader MUST 发出一个终结性 anchor_failover 后关闭其流)
前置:IUT 充当 C 的活跃 Anchor,cluster_epoch = E,已有一个订阅者;peer 模拟的 A2 将在 IUT 租约失效后于 E + 1 接管所有权。
操作:
topology.stream。A2 于 E + 1 取得所有权,并让该事实对 IUT 可观测。
通过条件:
anchor_state 事件:payload.field = "anchor_failover"、payload.details.reason = "active_lost"、successor_nid 为 A2 的 NID、cluster_epoch = E + 1。anchor_quorum_lost 线格式与降级只读运行Req:NPS-CR-0009 §3.3、NPS-2 §12.2 anchor_state 子类型 anchor_quorum_lost
前置:IUT 充当三 Anchor 集群的活跃 Anchor,quorum_size = 2;已公告一个成员;已有一个订阅者。
操作:
topology.stream。available = 1,低于 quorum_size = 2)。topology.snapshot(读),并另发起一次拓扑写。DiffFrame,event_type = "anchor_state"、payload.field = "anchor_quorum_lost"。payload.details 携带两个必填字段 quorum_size 与 available(均为 uint32),且 quorum_size = 2、available = 1,与 fixture 一致。AnnounceFrame 报告 health: "degraded"(NPS-4 §3.2)。NWP-ANCHOR-NOT-LEADER 被拒。anchor_state 事件其 cluster_epoch 严格大于失去 quorum 前的 epoch;不得以旧 epoch 恢复接受写。NWP-ANCHOR-NOT-LEADERReq:NPS-CR-0009 §3.1,错误码 NWP-ANCHOR-NOT-LEADER
前置:IUT 配置为 C 的 standby Anchor;peer 模拟的 Anchor A1 以 cluster_epoch = E 持有所有权。
操作:
topology:read 与写权限)向 IUT 推送一个会改变集群成员的拓扑写(DiffFrame / AnnounceFrame),携带 cluster_epoch = E。topology.snapshot。
通过条件:
NWP-ANCHOR-NOT-LEADER 的 ErrorFrame 拒绝,映射到 NPS-CLIENT-CONFLICT(HTTP 模式下为 409)。A1 并报告成功 —— standby MUST 拒绝,不得代理。cluster_epoch(≤ E)。standby MUST NOT 报告自己从未观测过的 epoch。NWP-ANCHOR-EPOCH-FENCEDReq:NPS-CR-0009 §3.1,错误码 NWP-ANCHOR-EPOCH-FENCED
前置:IUT 充当 C 的活跃 Anchor,自认持有 cluster_epoch = E。
操作:
cluster_epoch = E + 1 的入站拓扑帧 —— 即来自一个已在更高 epoch 下接管所有权的对端。cluster_epoch = E 外完全相同的帧。
通过条件:
NWP-ANCHOR-EPOCH-FENCED 的 ErrorFrame 拒绝(→ NPS-CLIENT-CONFLICT)。NWP-ANCHOR-NOT-LEADER 应答步骤 1 —— 两个错误码含义不同:NOT-LEADER 是”你写到了非 owner”,EPOCH-FENCED 是”接收方是被取代的 owner”。cluster_epoch 才围栏接收方。cluster_epoch 最高的 AnchorReq:NPS-CR-0009 §3.4、NPS-4 §9 多 Anchor 集群解析
前置:IUT 充当 NDP Registry(参考实现:nps-registry),无既有条目。
操作:
A1:cluster_anchor = C、cluster_epoch = 4。A2:cluster_anchor = C、cluster_epoch = 7;两条条目均存活且在 TTL 内。C 发送 ResolveFrame。cluster_epoch = 4 重新公告 A1(仍在心跳的陈旧 leader),再次解析 C。C2 公告一个省略 cluster_epoch 的 Anchor A3,同时公告 cluster_epoch = 2 的 A4,然后解析 C2。
通过条件:
A2 —— 存活条目中 cluster_epoch 最高者。cluster_epoch = 7。A2:Registry MUST NOT 把集群 C 降级到 epoch 4(按集群单调)。A4:省略 cluster_epoch 的公告按 epoch 1 处理,因而输给任何大于 1 的显式 epoch。NDP-CLUSTER-SPLITReq:NPS-CR-0009 §3.4,错误码 NDP-CLUSTER-SPLIT
前置:IUT 充当 NDP Registry,无既有条目。
操作:
A1:cluster_anchor = C、cluster_epoch = 5。A2:cluster_anchor = C、cluster_epoch = 5;两条条目均存活且在 TTL 内。C 发送 ResolveFrame。A1 的条目超过 TTL 老化,再次解析 C。
通过条件:
NDP-CLUSTER-SPLIT 的 ErrorFrame 应答(→ NPS-CLIENT-CONFLICT)。C。A2 并成功:只剩一条存活条目(或一侧被更高 epoch 取代)后,脑裂无需人工介入即自行消解。cluster_epoch = 1 且不发任何 HA 事件Req:NPS-CR-0009 §4 / §5(向后兼容)、NPS-2 §12.2 前置:IUT 充当其集群唯一的 Anchor,未配置多 Anchor HA —— 即默认的 L1/L2 部署形态。 操作:
topology.stream 并抓一次 topology.snapshot。cluster_epoch 的拓扑写 —— 模拟 alpha.17 之前的对端。subscribed 应答,要么省略 cluster_epoch,要么报告 cluster_epoch = 1;该值全程不增长,跨重启亦然。payload.field 为 "anchor_failover" 或 "anchor_quorum_lost" 的 anchor_state 事件。NWP-ANCHOR-NOT-LEADER 或 NWP-ANCHOR-EPOCH-FENCED。这些用例关闭原先仅 follow-up 跟踪的缺口。它们验证组装后的 AaaS 服务边界,
而不只是 SDK codec。L2-01..L2-05 为强制项,不得记 na。L2-06/07 为 SHOULD:
部署若有意识地不实现其中一项,可以记 na,但该 case 结果 MUST 携带非空
message 解释例外。组件证据与缺口索引见
aaas-l2-requirement-disposition.json。
要求:L2-01(MUST),NPS-5 §3.1 前置:IUT 暴露一个需要两个有序内部 worker 操作的 Action;harness 可捕获 IUT 到 orchestrator 的边界。 操作:发送合法 ActionFrame,并允许服务启动内部编排。 通过条件:
要求:L2-02(MUST),NPS-5 §3.1.2
前置:同 TC-N2-AaaS-01,并启用 TaskFrame 捕获。
操作:先携带合法 W3C traceparent 调用一次,再不带 trace 调用一次。
通过条件:
trace_id,携带新生成的 16 位 hex span_id 与入站 trace flags。trace_id 与 span_id。要求:L2-03(MUST),token-budget §2.1–§2.4 前置:IUT Action 返回确定的逻辑 payload,并接受 CGN-Estimate budget。 操作:用充足/不足 budget,分别通过 JSON 与另一种受支持 encoding 调用。 通过条件:
token_est,并标识 estimate 面而非 CGN-Billing。要求:L2-04(MUST),NPS-5 §4
前置:双 worker TaskFrame 设置 preflight=true;harness 可让任一 worker 可用或不可用。
操作:一个 worker 不可用运行一次,两个都可用再运行一次。
通过条件:
NOP-RESOURCE-INSUFFICIENT,且不 dispatch 任何 task node。(agent, action) 对。要求:L2-05(MUST),NPS-5 §3.5.2 前置:worker 可返回 retryable/non-retryable 失败,或超过 node/overall deadline。 操作:执行重试后成功、重试耗尽、不可重试错误、node timeout 与 overall timeout。 通过条件:
max_retries,并复用同一逻辑 subtask 与幂等身份。NOP-DELEGATE-TIMEOUT/NOP-TASK-TIMEOUT 终结;迟到结果不得改写终态。要求:L2-06(SHOULD),NPS-2 §7.2–§7.3
前置:IUT 声明一个 Action,并具有可查询 task store。
操作:提交 ActionFrame.async=true,轮询 system.task.status,并取消另一个运行中的 task。
通过条件:
task_id、poll_url 与 request_id。cancelled;终态与未知 task 负路径返回规定错误。要求:L2-07(SHOULD),NPS-5 §3.4.1–§3.4.2 前置:worker 与 orchestrator 通过可控 CGN window 交换多帧 AlignStream。 操作:耗尽 window,以反向帧恢复,ACK 已消费序号,再分别 NAK 一个保留与一个已淘汰序号。 通过条件:
ack_seq 推进保留窗口;可解析 nak_seq 从请求序号开始重放且不乱序。NOP-STREAM-NAK-UNRESOLVABLE,不得静默跳帧。合规跑结产出一份 manifest(JSON)汇总每个用例的结果。manifest 嵌入到
NPS-NODE-L2-CERTIFIED.md:
{
"profile": "NPS-Node-L2",
"profile_version": "0.7",
"scope": ["L2-01", "L2-02", "L2-03", "L2-04", "L2-05", "L2-08"],
"iut": {
"name": "example-anchor",
"version": "0.1.0",
"nid": "urn:nps:node:example.com:anchor-01"
},
"peer": {
"name": "nps-dotnet-reference",
"version": "1.0.0-alpha.11"
},
"run": {
"date": "2026-04-27T00:00:00Z",
"environment": "linux-x64 / 1 vCPU / 1 GB"
},
"cases": [
{ "id": "TC-N2-AaaS-01", "result": "pass" },
{ "id": "TC-N2-AaaS-02", "result": "pass" },
{ "id": "TC-N2-AaaS-03", "result": "pass" },
{ "id": "TC-N2-AaaS-04", "result": "pass" },
{ "id": "TC-N2-AaaS-05", "result": "pass" },
{ "id": "TC-N2-AaaS-06", "result": "na", "message": "有记录的 SHOULD 例外:本服务仅暴露同步 Action。" },
{ "id": "TC-N2-AaaS-07", "result": "na", "message": "有记录的 SHOULD 例外:本服务仅发送最终结果,不声明 AlignStream。" },
{ "id": "TC-N2-AnchorTopo-01", "result": "pass" },
{ "id": "TC-N2-AnchorTopo-02", "result": "pass" },
{ "id": "TC-N2-AnchorTopo-03", "result": "pass" },
{ "id": "TC-N2-AnchorTopo-04", "result": "pass" },
{ "id": "TC-N2-AnchorTopo-05", "result": "pass" },
{ "id": "TC-N2-AnchorTopo-06", "result": "pass" },
{ "id": "TC-N2-AnchorTopo-07", "result": "pass" },
{ "id": "TC-N2-AnchorTopo-08", "result": "pass" },
{ "id": "TC-N2-AnchorStream-01", "result": "pass" },
{ "id": "TC-N2-AnchorStream-02", "result": "pass" },
{ "id": "TC-N2-AnchorStream-03", "result": "pass" },
{ "id": "TC-N2-AnchorStream-04", "result": "pass" },
{ "id": "TC-N2-Tls-01", "result": "pass" },
{ "id": "TC-N2-Tls-02", "result": "pass" },
{ "id": "TC-N2-Tls-03", "result": "pass" },
{ "id": "TC-N2-Tls-04", "result": "pass" },
{ "id": "TC-N2-BridgeIn-01", "result": "na" },
{ "id": "TC-N2-BridgeIn-02", "result": "na" },
{ "id": "TC-N2-BridgeIn-03", "result": "na" },
{ "id": "TC-N2-BridgeIn-04", "result": "na" },
{ "id": "TC-N2-BridgeIn-05", "result": "na" },
{ "id": "TC-N2-BridgeIn-06", "result": "na" },
{ "id": "TC-N2-HA-01", "result": "na" },
{ "id": "TC-N2-HA-02", "result": "na" },
{ "id": "TC-N2-HA-03", "result": "na" },
{ "id": "TC-N2-HA-04", "result": "na" },
{ "id": "TC-N2-HA-05", "result": "na" },
{ "id": "TC-N2-HA-06", "result": "na" },
{ "id": "TC-N2-HA-07", "result": "na" },
{ "id": "TC-N2-HA-08", "result": "na" },
{ "id": "TC-N2-HA-09", "result": "pass" }
],
"summary": { "pass": 22, "fail": 0, "skip": 0, "na": 16 }
}
上面的示例是一个单 Anchor AaaS 服务:它通过五个强制 L2-01..L2-05 用例,
为 L2-06/07 记录明确 SHOULD 例外,并通过 12 个拓扑与 4 个 TLS 用例;它不是 Bridge、
不跑 Registry、也未声明多 Anchor HA。因此 TC-N2-HA-01..08 记 na,
TC-N2-HA-09 通过。
认证按用例族授予,族内全有或全无:
TC-N2-AaaS-01..05)—— 完整 AaaS L2 声明要求五项全部
pass;na、skip 与仅组件证据都不充分。TC-N2-AaaS-06..07)—— 每项 SHOULD pass。只有对应用例
message 非空并记录实现方的有理由例外时,才接受 na。TC-N2-AnchorTopo-*、TC-N2-AnchorStream-*)—— 任何 L2 主张都要求
12 个用例全部 pass。本族没有可选用例:要么实现 NPS-2 §12
定义的 topology.snapshot / topology.stream,要么不实现。TC-N2-Tls-*)—— 终结原生模式 NCP-over-TLS 的 IUT 要求 4 个
全部 pass;否则记 na。TC-N2-BridgeIn-*)—— 声明了非空 bridge_inbound_protocols 的 IUT
要求 6 个全部 pass;outbound-only 或非 Bridge IUT 记 na。TC-N2-HA-01..TC-N2-HA-06)—— 声明多 Anchor HA 的 IUT
(AaaS Profile L3 能力,NPS-CR-0009 §4)要求
6 个全部 pass;单 Anchor IUT 记 na。TC-N2-HA-07、TC-N2-HA-08)—— 实现 NDP Registry 解析面
的 IUT 要求 2 个全部 pass;仅 Anchor 角色的 IUT 记 na。TC-N2-HA-09)—— 单 Anchor IUT MUST pass;声明多 Anchor HA 的
IUT 记 na。合法 manifest 中本用例与 Anchor 侧族恰有一方为 na —— 二者互斥,
不得同时 na,也不得同时 pass。某个族整体记 na 不阻塞其他族的认证;族内部分 na 属于 manifest 错误。
这七个 AaaS 用例属于当前 alpha.20 前合同。不得从 scope 删除,也不得作为
future-CR 豁免处理。
| 语言 | 路径 | 状态 |
|---|---|---|
| .NET 10(xUnit),Anchor 拓扑 | impl/dotnet/tests/NPS.Tests/Nwp/Anchor/AnchorTopologyTests.cs |
已实现 |
| .NET 10(xUnit),NCP-over-TLS ingress | tools/daemons/nps-ingress/tests/IngressTlsConformanceTests.cs |
已实现;TC-N2-Tls-01..04 |
| Python | impl/python/tests/conformance/node_l2/ |
TODO(Phase 2) |
| TypeScript | impl/typescript/tests/conformance/node-l2/ |
TODO(Phase 2) |
参考套件的测试名 MUST 与上方 TC-N2-* ID 保持一致,确保跑结报告与 §4 manifest 1:1 对齐。
| 版本 | 日期 | 变更 |
|---|---|---|
| 0.7 | 2026-09-05 | 为当前 AaaS L2-01..L2-07 增加稳定 TC-N2-AaaS-01..07,不再整体延期。L2-01..05 为强制项;L2-06/07 仅允许有理由的 SHOULD 例外。manifest 扩展到 38 个 case,并链接 alpha.19 机器可读处置。 |
| 0.6 | 2026-08-31 | 修正 TC-N2-Tls-03..04,使其执行 NPS-1 §2.6 的规范性交互序列:preamble + Hello、转发后端 Caps、随后发送 Ident。避免仅测试 pipeline 情形,从而漏掉正常 Caps 后路径中的死锁或证书/Ident 绑定绕过。 |
| 0.5 | 2026-08-01 | 新增 §3.4 多 Anchor 高可用(TC-N2-HA-01..09),落地 NPS-CR-0009 §4 承诺的 TC-N2-HA-* 族:两个拓扑读表面均携带 cluster_epoch、计划内交接与活跃方失联两种 anchor_failover 线格式(后者为终结事件并关闭流)、anchor_quorum_lost 线格式及降级只读运行与新 epoch 恢复、standby 写 → NWP-ANCHOR-NOT-LEADER、被取代 leader → NWP-ANCHOR-EPOCH-FENCED、NDP 最高 epoch 解析且不降级、同 epoch 脑裂 → NDP-CLUSTER-SPLIT、单 Anchor 在 cluster_epoch = 1 下的向后兼容。§2 增加”多 Anchor 前置”行;§4 manifest 现枚举四个用例族(12 拓扑 + 4 TLS + 6 bridge + 9 HA),并定义 HA Anchor 侧与单 Anchor 向后兼容用例的互斥关系。CR-0009 留白的两点(低 epoch 入站帧;quorum 恢复所用的 anchor_state 子类型)在 §3.4 中显式标注而非断言。 |
| 0.4 | 2026-07-23 | 新增 §3.3 Bridge Node 入站(TC-N2-BridgeIn-01..06),落地 NPS-CR-0010 §4 承诺的 TC-N2-BRIDGE-IN-* 族:MCP 完整方法集(含 resources/*)、gRPC + A2A 入站往返、裸名/限定名解析、§16.3 错误映射保真、NWP-BRIDGE-DIRECTION-UNSUPPORTED 拒绝。§4 manifest 枚举全部三个用例族(12 拓扑 + 4 TLS + 6 bridge),按族认证并定义 na 语义。同时补记缺失的 0.3 变更行。 |
| 0.3 | 2026-06-12 | (补记行 —— 该变更随 alpha.13 发布时未写变更历史。)新增 §3.2 NCP-over-TLS 入站终结(TC-N2-Tls-01..04),验证 NPS-RFC-0006 §6 准入门:ALPN nps/1.0、mTLS 强制、信任锚验证 + 会话 NID 绑定、IdentFrame/证书 NID 不一致时 NCP-NID-MISMATCH。 |
| 0.2 | 2026-05-01 | 新增 5 个负路径测试用例(TC-N2-AnchorTopo-04 至 -08),强制执行”每个 MUST 拒绝子句有一个失败路径 TC”标准:无 topology:read 的未鉴权访问(NWP-TOPOLOGY-UNAUTHORIZED)、depth 超过 Anchor 上限(NWP-TOPOLOGY-DEPTH-UNSUPPORTED)、不识别的 scope(NWP-TOPOLOGY-UNSUPPORTED-SCOPE)、不识别的 filter key(NWP-TOPOLOGY-FILTER-UNSUPPORTED)、不识别的 reserved type(NWP-RESERVED-TYPE-UNSUPPORTED)。TC 总数 7 → 12;认证通过阈值同步更新。修正 TC-N2-AnchorTopo-01 和 -03 中 node_kind → node_roles(M1 一致性)。 |
| 0.1 | 2026-04-27 | 初始草案:覆盖 L2-08(topology.snapshot / topology.stream,NPS-CR-0002)的 7 个用例。paired-peer 方法论沿用 L1。 |
Copyright: LabAcacia / INNO LOTUS PTY LTD · Apache 2.0