mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-15 20:05:13 +02:00
refactor(dynamic): enhance resolver detection for frameworks, refine SSA receiver validation, and expand test coverage
This commit is contained in:
parent
3027c1afa7
commit
f49211d788
38 changed files with 1198 additions and 137 deletions
16
tests/fixtures/fp_guards/broker_adapter_collisions/expectations.json
vendored
Normal file
16
tests/fixtures/fp_guards/broker_adapter_collisions/expectations.json
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"required_findings": [],
|
||||
"forbidden_findings": [
|
||||
{ "id_prefix": "taint-unsanitised-flow" }
|
||||
],
|
||||
"noise_budget": {
|
||||
"max_total_findings": 0,
|
||||
"max_high_findings": 0
|
||||
},
|
||||
"performance_expectations": {
|
||||
"max_ms_no_index": 1000,
|
||||
"max_ms_index_cold": 1500,
|
||||
"max_ms_index_warm": 500,
|
||||
"ci_mode": "lenient"
|
||||
}
|
||||
}
|
||||
19
tests/fixtures/fp_guards/broker_adapter_collisions/node_non_sqs_send.js
vendored
Normal file
19
tests/fixtures/fp_guards/broker_adapter_collisions/node_non_sqs_send.js
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
const { SQSClient } = require("@aws-sdk/client-sqs");
|
||||
|
||||
class MetricsPublisher {
|
||||
send(event) {
|
||||
return Promise.resolve({ ok: true, event });
|
||||
}
|
||||
}
|
||||
|
||||
const sqs = new SQSClient({});
|
||||
const metrics = new MetricsPublisher();
|
||||
|
||||
function handler(event) {
|
||||
return metrics.send({
|
||||
type: "delivery_attempt",
|
||||
requestId: event.requestId,
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { handler, sqs };
|
||||
16
tests/fixtures/fp_guards/broker_adapter_collisions/python_non_broker_handler.py
vendored
Normal file
16
tests/fixtures/fp_guards/broker_adapter_collisions/python_non_broker_handler.py
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import boto3
|
||||
|
||||
|
||||
sqs = boto3.client("sqs")
|
||||
|
||||
|
||||
class AuditCache:
|
||||
def process_message(self, envelope):
|
||||
return {"stored": True, "id": envelope.get("id")}
|
||||
|
||||
|
||||
cache = AuditCache()
|
||||
|
||||
|
||||
def handler(envelope):
|
||||
return cache.process_message(envelope)
|
||||
13
tests/fixtures/fp_guards/broker_adapter_collisions/python_non_rabbit_process.py
vendored
Normal file
13
tests/fixtures/fp_guards/broker_adapter_collisions/python_non_rabbit_process.py
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import pika
|
||||
|
||||
|
||||
class ReportWorker:
|
||||
def process(self, report):
|
||||
return {"status": "queued", "report_id": report.get("id")}
|
||||
|
||||
|
||||
worker = ReportWorker()
|
||||
|
||||
|
||||
def process(report):
|
||||
return worker.process(report)
|
||||
16
tests/fixtures/fp_guards/phase21_adapter_collisions/expectations.json
vendored
Normal file
16
tests/fixtures/fp_guards/phase21_adapter_collisions/expectations.json
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"required_findings": [],
|
||||
"forbidden_findings": [
|
||||
{ "id_prefix": "taint-unsanitised-flow" }
|
||||
],
|
||||
"noise_budget": {
|
||||
"max_total_findings": 0,
|
||||
"max_high_findings": 0
|
||||
},
|
||||
"performance_expectations": {
|
||||
"max_ms_no_index": 1000,
|
||||
"max_ms_index_cold": 1500,
|
||||
"max_ms_index_warm": 500,
|
||||
"ci_mode": "lenient"
|
||||
}
|
||||
}
|
||||
14
tests/fixtures/fp_guards/phase21_adapter_collisions/go_gqlgen_helper.go
vendored
Normal file
14
tests/fixtures/fp_guards/phase21_adapter_collisions/go_gqlgen_helper.go
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package graph
|
||||
|
||||
import "context"
|
||||
|
||||
// import "github.com/99designs/gqlgen/graphql"
|
||||
type queryResolver struct{}
|
||||
|
||||
func (r *queryResolver) User(ctx context.Context, id string) (string, error) {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func NormalizeID(id string) string {
|
||||
return id
|
||||
}
|
||||
15
tests/fixtures/fp_guards/phase21_adapter_collisions/java_quartz_queue_schedule.java
vendored
Normal file
15
tests/fixtures/fp_guards/phase21_adapter_collisions/java_quartz_queue_schedule.java
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import org.quartz.Job;
|
||||
import org.quartz.JobExecutionContext;
|
||||
|
||||
class TickJob implements Job {
|
||||
public void execute(JobExecutionContext context) {}
|
||||
|
||||
public void enqueue(Object payload) {
|
||||
NotificationQueue queue = new NotificationQueue();
|
||||
queue.scheduleJob(payload);
|
||||
}
|
||||
}
|
||||
|
||||
class NotificationQueue {
|
||||
void scheduleJob(Object payload) {}
|
||||
}
|
||||
11
tests/fixtures/fp_guards/phase21_adapter_collisions/java_spring_middleware_helper.java
vendored
Normal file
11
tests/fixtures/fp_guards/phase21_adapter_collisions/java_spring_middleware_helper.java
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
class AuditInterceptor implements HandlerInterceptor {
|
||||
public boolean preHandle(Object request, Object response, Object handler) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public String normalize(String payload) {
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
11
tests/fixtures/fp_guards/phase21_adapter_collisions/js_relay_helper.js
vendored
Normal file
11
tests/fixtures/fp_guards/phase21_adapter_collisions/js_relay_helper.js
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
const { nodeDefinitions } = require('graphql-relay');
|
||||
|
||||
function resolveNode(globalId) {
|
||||
return globalId;
|
||||
}
|
||||
|
||||
function normalizeId(id) {
|
||||
return String(id);
|
||||
}
|
||||
|
||||
module.exports = { resolveNode, normalizeId, nodeDefinitions };
|
||||
15
tests/fixtures/fp_guards/phase21_adapter_collisions/js_sequelize_helper.js
vendored
Normal file
15
tests/fixtures/fp_guards/phase21_adapter_collisions/js_sequelize_helper.js
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
module.exports = {
|
||||
async up(queryInterface, Sequelize) {
|
||||
await queryInterface.createTable('users', {});
|
||||
},
|
||||
|
||||
async down(queryInterface, Sequelize) {
|
||||
await queryInterface.dropTable('users');
|
||||
},
|
||||
};
|
||||
|
||||
function normalizeName(name) {
|
||||
return String(name);
|
||||
}
|
||||
|
||||
module.exports.normalizeName = normalizeName;
|
||||
9
tests/fixtures/fp_guards/phase21_adapter_collisions/php_laravel_bootstrapper.php
vendored
Normal file
9
tests/fixtures/fp_guards/phase21_adapter_collisions/php_laravel_bootstrapper.php
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
class Bootstrapper
|
||||
{
|
||||
public function configure($app)
|
||||
{
|
||||
return $app->withMiddleware([]);
|
||||
}
|
||||
}
|
||||
11
tests/fixtures/fp_guards/phase21_adapter_collisions/python_alembic_helper.py
vendored
Normal file
11
tests/fixtures/fp_guards/phase21_adapter_collisions/python_alembic_helper.py
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
from alembic import op
|
||||
|
||||
revision = "abc123def4"
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table("users")
|
||||
|
||||
|
||||
def normalize_name(name):
|
||||
return str(name)
|
||||
16
tests/fixtures/fp_guards/phase21_adapter_collisions/python_celery_mailer_delay.py
vendored
Normal file
16
tests/fixtures/fp_guards/phase21_adapter_collisions/python_celery_mailer_delay.py
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from celery import shared_task
|
||||
|
||||
|
||||
@shared_task
|
||||
def tick(payload):
|
||||
return payload
|
||||
|
||||
|
||||
class Mailer:
|
||||
def delay(self, payload):
|
||||
return payload
|
||||
|
||||
|
||||
def enqueue(payload):
|
||||
mailer = Mailer()
|
||||
return mailer.delay(payload)
|
||||
10
tests/fixtures/fp_guards/phase21_adapter_collisions/python_channels_helper.py
vendored
Normal file
10
tests/fixtures/fp_guards/phase21_adapter_collisions/python_channels_helper.py
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from channels.generic.websocket import WebsocketConsumer
|
||||
|
||||
|
||||
class ChatConsumer(WebsocketConsumer):
|
||||
def receive(self, text_data=None, bytes_data=None):
|
||||
return text_data
|
||||
|
||||
|
||||
def normalize_frame(text_data):
|
||||
return str(text_data)
|
||||
10
tests/fixtures/fp_guards/phase21_adapter_collisions/python_django_middleware_helper.py
vendored
Normal file
10
tests/fixtures/fp_guards/phase21_adapter_collisions/python_django_middleware_helper.py
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from django.utils.deprecation import MiddlewareMixin
|
||||
|
||||
|
||||
class AuditMiddleware(MiddlewareMixin):
|
||||
def process_request(self, request):
|
||||
return None
|
||||
|
||||
|
||||
def normalize_request(request):
|
||||
return request
|
||||
11
tests/fixtures/fp_guards/phase21_adapter_collisions/python_django_migration_helper.py
vendored
Normal file
11
tests/fixtures/fp_guards/phase21_adapter_collisions/python_django_migration_helper.py
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
operations = [
|
||||
migrations.CreateModel(name="User", fields=[]),
|
||||
]
|
||||
|
||||
|
||||
def normalize_name(name):
|
||||
return str(name)
|
||||
12
tests/fixtures/fp_guards/phase21_adapter_collisions/python_graphene_helper.py
vendored
Normal file
12
tests/fixtures/fp_guards/phase21_adapter_collisions/python_graphene_helper.py
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import graphene
|
||||
|
||||
|
||||
class Query(graphene.ObjectType):
|
||||
user = graphene.String()
|
||||
|
||||
def resolve_user(self, info, id):
|
||||
return id
|
||||
|
||||
|
||||
def normalize_id(raw):
|
||||
return str(raw)
|
||||
12
tests/fixtures/fp_guards/phase21_adapter_collisions/python_socketio_helper.py
vendored
Normal file
12
tests/fixtures/fp_guards/phase21_adapter_collisions/python_socketio_helper.py
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import socketio
|
||||
|
||||
sio = socketio.Server()
|
||||
|
||||
|
||||
@sio.on("message")
|
||||
def message(sid, data):
|
||||
return data
|
||||
|
||||
|
||||
def normalize(data):
|
||||
return str(data)
|
||||
13
tests/fixtures/fp_guards/phase21_adapter_collisions/ruby_actioncable_helper.rb
vendored
Normal file
13
tests/fixtures/fp_guards/phase21_adapter_collisions/ruby_actioncable_helper.rb
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
class ChatChannel < ApplicationCable::Channel
|
||||
def subscribed
|
||||
stream_from "chat_room"
|
||||
end
|
||||
|
||||
def receive(data)
|
||||
data
|
||||
end
|
||||
|
||||
def normalize(data)
|
||||
data.to_s
|
||||
end
|
||||
end
|
||||
14
tests/fixtures/fp_guards/phase21_adapter_collisions/rust_juniper_helper.rs
vendored
Normal file
14
tests/fixtures/fp_guards/phase21_adapter_collisions/rust_juniper_helper.rs
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
use juniper::graphql_object;
|
||||
|
||||
pub struct Query;
|
||||
|
||||
#[graphql_object]
|
||||
impl Query {
|
||||
fn user(&self, id: String) -> String {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
pub fn normalize_id(id: &str) -> String {
|
||||
id.to_string()
|
||||
}
|
||||
|
|
@ -926,6 +926,27 @@ fn fp_guard_framework_express_res_json() {
|
|||
validate_expectations(&diags, &dir);
|
||||
}
|
||||
|
||||
/// FP guard, broker-adapter receiver collisions: OSS-shaped handlers named
|
||||
/// `handler` / `process` and a non-SQS `.send(...)` publisher must stay
|
||||
/// ordinary helper code unless receiver facts prove the call is on a broker
|
||||
/// runtime object.
|
||||
#[test]
|
||||
fn fp_guard_broker_adapter_receiver_collisions() {
|
||||
let dir = fixture_path("fp_guards/broker_adapter_collisions");
|
||||
let diags = scan_fixture_dir(&dir, AnalysisMode::Full);
|
||||
validate_expectations(&diags, &dir);
|
||||
}
|
||||
|
||||
/// FP guard, Phase 21 adapter collisions: framework-marked files can contain
|
||||
/// ordinary helpers, controller bootstrappers, mailer queues, and migration
|
||||
/// formatting functions that must not be promoted to dynamic entry kinds.
|
||||
#[test]
|
||||
fn fp_guard_phase21_adapter_collisions() {
|
||||
let dir = fixture_path("fp_guards/phase21_adapter_collisions");
|
||||
let diags = scan_fixture_dir(&dir, AnalysisMode::Full);
|
||||
validate_expectations(&diags, &dir);
|
||||
}
|
||||
|
||||
/// FP guard, FastAPI `dependencies=[Depends(requires_access_*)]`
|
||||
/// route-level guard short-circuits `auth_check_covers_subject` so
|
||||
/// the handler body's path-param ORM calls and row-variable method
|
||||
|
|
|
|||
|
|
@ -17,11 +17,15 @@
|
|||
mod common;
|
||||
|
||||
use nyx_scanner::dynamic::framework::registry::adapters_for;
|
||||
use nyx_scanner::dynamic::framework::{FrameworkBinding, detect_binding};
|
||||
use nyx_scanner::dynamic::framework::{
|
||||
FrameworkBinding, detect_binding, detect_binding_with_context,
|
||||
};
|
||||
use nyx_scanner::dynamic::lang;
|
||||
use nyx_scanner::dynamic::spec::{EntryKind, EntryKindTag, HarnessSpec, PayloadSlot};
|
||||
use nyx_scanner::labels::Cap;
|
||||
use nyx_scanner::summary::CalleeSite;
|
||||
use nyx_scanner::summary::FuncSummary;
|
||||
use nyx_scanner::summary::ssa_summary::SsaFuncSummary;
|
||||
use nyx_scanner::symbol::Lang;
|
||||
|
||||
const SUPPORTED_LANGS: &[Lang] = &[
|
||||
|
|
@ -215,6 +219,39 @@ fn detect_from_bytes(lang: Lang, bytes: &[u8], handler: &str) -> Option<Framewor
|
|||
detect_binding(&summary, tree.root_node(), bytes, lang)
|
||||
}
|
||||
|
||||
fn detect_collision_fixture_with_receiver(
|
||||
lang: Lang,
|
||||
fixture: &str,
|
||||
handler: &str,
|
||||
callee: &str,
|
||||
receiver: &str,
|
||||
receiver_ty: &str,
|
||||
) -> Option<FrameworkBinding> {
|
||||
let bytes = std::fs::read(
|
||||
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests/fixtures/fp_guards/broker_adapter_collisions")
|
||||
.join(fixture),
|
||||
)
|
||||
.expect("collision fixture exists");
|
||||
let ts_lang = ts_language_for(lang);
|
||||
let mut parser = tree_sitter::Parser::new();
|
||||
parser.set_language(&ts_lang).unwrap();
|
||||
let tree = parser.parse(&bytes, None).unwrap();
|
||||
let mut summary = FuncSummary {
|
||||
name: handler.into(),
|
||||
..Default::default()
|
||||
};
|
||||
summary.callees.push(CalleeSite {
|
||||
name: callee.to_owned(),
|
||||
receiver: Some(receiver.to_owned()),
|
||||
ordinal: 0,
|
||||
..Default::default()
|
||||
});
|
||||
let mut ssa = SsaFuncSummary::default();
|
||||
ssa.typed_call_receivers.push((0, receiver_ty.to_owned()));
|
||||
detect_binding_with_context(&summary, Some(&ssa), tree.root_node(), &bytes, lang)
|
||||
}
|
||||
|
||||
fn middleware_names(binding: &FrameworkBinding) -> Vec<String> {
|
||||
binding
|
||||
.middleware
|
||||
|
|
@ -417,6 +454,51 @@ def on_message(ch, method, properties, body):\n validate_request(body)\n",
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn phase20_broker_adapter_receiver_collisions_have_fixture_anchors() {
|
||||
let cases: &[(Lang, &str, &str, &str, &str, &str)] = &[
|
||||
(
|
||||
Lang::Python,
|
||||
"python_non_broker_handler.py",
|
||||
"handler",
|
||||
"cache.process_message",
|
||||
"cache",
|
||||
"AuditCache",
|
||||
),
|
||||
(
|
||||
Lang::Python,
|
||||
"python_non_rabbit_process.py",
|
||||
"process",
|
||||
"worker.process",
|
||||
"worker",
|
||||
"ReportWorker",
|
||||
),
|
||||
(
|
||||
Lang::JavaScript,
|
||||
"node_non_sqs_send.js",
|
||||
"handler",
|
||||
"metrics.send",
|
||||
"metrics",
|
||||
"MetricsPublisher",
|
||||
),
|
||||
];
|
||||
|
||||
for (lang, fixture, handler, callee, receiver, receiver_ty) in cases {
|
||||
let binding = detect_collision_fixture_with_receiver(
|
||||
*lang,
|
||||
fixture,
|
||||
handler,
|
||||
callee,
|
||||
receiver,
|
||||
receiver_ty,
|
||||
);
|
||||
assert!(
|
||||
binding.is_none(),
|
||||
"{fixture} should not bind as a broker message handler; got {binding:?}",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn registry_slices_include_phase_20_adapters() {
|
||||
let java_names: Vec<&'static str> = adapters_for(Lang::Java).iter().map(|a| a.name()).collect();
|
||||
|
|
|
|||
|
|
@ -26,7 +26,8 @@ use nyx_scanner::dynamic::lang;
|
|||
use nyx_scanner::dynamic::spec::{EntryKind, EntryKindTag, HarnessSpec, PayloadSlot};
|
||||
use nyx_scanner::evidence::EntryKind as EvEntryKind;
|
||||
use nyx_scanner::labels::Cap;
|
||||
use nyx_scanner::summary::FuncSummary;
|
||||
use nyx_scanner::summary::ssa_summary::SsaFuncSummary;
|
||||
use nyx_scanner::summary::{CalleeSite, FuncSummary};
|
||||
use nyx_scanner::symbol::Lang;
|
||||
|
||||
fn make_spec(lang: Lang, kind: EvEntryKind, entry_name: &str, entry_file: &str) -> HarnessSpec {
|
||||
|
|
@ -91,6 +92,46 @@ fn run_adapter(
|
|||
.unwrap_or_else(|| panic!("{} did not fire on {fixture}", adapter.name()))
|
||||
}
|
||||
|
||||
fn detect_phase21_fp_fixture(
|
||||
adapter: &dyn FrameworkAdapter,
|
||||
lang: Lang,
|
||||
handler: &str,
|
||||
fixture: &str,
|
||||
typed_call: Option<(&str, &str, &str)>,
|
||||
) -> Option<FrameworkBinding> {
|
||||
let bytes = std::fs::read(
|
||||
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests/fixtures/fp_guards/phase21_adapter_collisions")
|
||||
.join(fixture),
|
||||
)
|
||||
.unwrap_or_else(|e| panic!("read Phase 21 FP fixture {fixture}: {e}"));
|
||||
let tree = parse(lang, &bytes);
|
||||
let mut summary = FuncSummary {
|
||||
name: handler.into(),
|
||||
..Default::default()
|
||||
};
|
||||
let mut ssa = SsaFuncSummary::default();
|
||||
if let Some((callee, receiver, receiver_ty)) = typed_call {
|
||||
summary.callees.push(CalleeSite {
|
||||
name: callee.to_owned(),
|
||||
receiver: Some(receiver.to_owned()),
|
||||
ordinal: 0,
|
||||
..Default::default()
|
||||
});
|
||||
ssa.typed_call_receivers.push((0, receiver_ty.to_owned()));
|
||||
}
|
||||
let ssa_ref = typed_call.is_some().then_some(&ssa);
|
||||
adapter.detect_with_context(&summary, ssa_ref, tree.root_node(), &bytes)
|
||||
}
|
||||
|
||||
struct Phase21FpCase<'a> {
|
||||
adapter: &'a dyn FrameworkAdapter,
|
||||
lang: Lang,
|
||||
handler: &'a str,
|
||||
fixture: &'a str,
|
||||
typed_call: Option<(&'a str, &'a str, &'a str)>,
|
||||
}
|
||||
|
||||
// ── Supported-set assertions ──────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
|
|
@ -447,6 +488,134 @@ fn migration_prisma_adapter_binds_vuln_fixture() {
|
|||
assert_eq!(b.adapter, "migration-prisma");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn phase21_adapter_collision_fixtures_do_not_bind() {
|
||||
let cases = [
|
||||
Phase21FpCase {
|
||||
adapter: &ScheduledCeleryAdapter,
|
||||
lang: Lang::Python,
|
||||
handler: "enqueue",
|
||||
fixture: "python_celery_mailer_delay.py",
|
||||
typed_call: Some(("mailer.delay", "mailer", "Mailer")),
|
||||
},
|
||||
Phase21FpCase {
|
||||
adapter: &ScheduledQuartzAdapter,
|
||||
lang: Lang::Java,
|
||||
handler: "enqueue",
|
||||
fixture: "java_quartz_queue_schedule.java",
|
||||
typed_call: Some(("queue.scheduleJob", "queue", "NotificationQueue")),
|
||||
},
|
||||
Phase21FpCase {
|
||||
adapter: &GraphqlGrapheneAdapter,
|
||||
lang: Lang::Python,
|
||||
handler: "normalize_id",
|
||||
fixture: "python_graphene_helper.py",
|
||||
typed_call: None,
|
||||
},
|
||||
Phase21FpCase {
|
||||
adapter: &GraphqlGqlgenAdapter,
|
||||
lang: Lang::Go,
|
||||
handler: "NormalizeID",
|
||||
fixture: "go_gqlgen_helper.go",
|
||||
typed_call: None,
|
||||
},
|
||||
Phase21FpCase {
|
||||
adapter: &GraphqlJuniperAdapter,
|
||||
lang: Lang::Rust,
|
||||
handler: "normalize_id",
|
||||
fixture: "rust_juniper_helper.rs",
|
||||
typed_call: None,
|
||||
},
|
||||
Phase21FpCase {
|
||||
adapter: &GraphqlRelayAdapter,
|
||||
lang: Lang::JavaScript,
|
||||
handler: "normalizeId",
|
||||
fixture: "js_relay_helper.js",
|
||||
typed_call: None,
|
||||
},
|
||||
Phase21FpCase {
|
||||
adapter: &WebsocketSocketIoAdapter,
|
||||
lang: Lang::Python,
|
||||
handler: "normalize",
|
||||
fixture: "python_socketio_helper.py",
|
||||
typed_call: None,
|
||||
},
|
||||
Phase21FpCase {
|
||||
adapter: &WebsocketChannelsAdapter,
|
||||
lang: Lang::Python,
|
||||
handler: "normalize_frame",
|
||||
fixture: "python_channels_helper.py",
|
||||
typed_call: None,
|
||||
},
|
||||
Phase21FpCase {
|
||||
adapter: &WebsocketActionCableAdapter,
|
||||
lang: Lang::Ruby,
|
||||
handler: "normalize",
|
||||
fixture: "ruby_actioncable_helper.rb",
|
||||
typed_call: None,
|
||||
},
|
||||
Phase21FpCase {
|
||||
adapter: &MiddlewareDjangoAdapter,
|
||||
lang: Lang::Python,
|
||||
handler: "normalize_request",
|
||||
fixture: "python_django_middleware_helper.py",
|
||||
typed_call: None,
|
||||
},
|
||||
Phase21FpCase {
|
||||
adapter: &MiddlewareLaravelAdapter,
|
||||
lang: Lang::Php,
|
||||
handler: "configure",
|
||||
fixture: "php_laravel_bootstrapper.php",
|
||||
typed_call: Some(("app.withMiddleware", "app", "ApplicationBuilder")),
|
||||
},
|
||||
Phase21FpCase {
|
||||
adapter: &MiddlewareSpringAdapter,
|
||||
lang: Lang::Java,
|
||||
handler: "normalize",
|
||||
fixture: "java_spring_middleware_helper.java",
|
||||
typed_call: None,
|
||||
},
|
||||
Phase21FpCase {
|
||||
adapter: &MigrationDjangoAdapter,
|
||||
lang: Lang::Python,
|
||||
handler: "normalize_name",
|
||||
fixture: "python_django_migration_helper.py",
|
||||
typed_call: None,
|
||||
},
|
||||
Phase21FpCase {
|
||||
adapter: &MigrationFlaskAdapter,
|
||||
lang: Lang::Python,
|
||||
handler: "normalize_name",
|
||||
fixture: "python_alembic_helper.py",
|
||||
typed_call: None,
|
||||
},
|
||||
Phase21FpCase {
|
||||
adapter: &MigrationSequelizeAdapter,
|
||||
lang: Lang::JavaScript,
|
||||
handler: "normalizeName",
|
||||
fixture: "js_sequelize_helper.js",
|
||||
typed_call: None,
|
||||
},
|
||||
];
|
||||
|
||||
for case in cases {
|
||||
let binding = detect_phase21_fp_fixture(
|
||||
case.adapter,
|
||||
case.lang,
|
||||
case.handler,
|
||||
case.fixture,
|
||||
case.typed_call,
|
||||
);
|
||||
assert!(
|
||||
binding.is_none(),
|
||||
"{fixture}::{handler} should not bind through {}; got {binding:?}",
|
||||
case.adapter.name(),
|
||||
fixture = case.fixture,
|
||||
handler = case.handler,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Harness emit shape ────────────────────────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue