@@ -509,6 +509,80 @@ def test_xray_subscription_uses_host_specific_template_override(access_token):
509509 delete_core (access_token , core ["id" ])
510510
511511
512+ def test_xray_subscription_template_override_isolated_per_host (access_token ):
513+ core = create_core (access_token )
514+ inbound = get_inbounds (access_token )[0 ]
515+ override_template = create_client_template (
516+ access_token ,
517+ name = unique_name ("xray_host_isolated_template" ),
518+ template_type = "xray_subscription" ,
519+ content = json .dumps (
520+ {
521+ "log" : {"loglevel" : "warning" },
522+ "inbounds" : [{"tag" : "placeholder" , "protocol" : "vmess" , "settings" : {"clients" : []}}],
523+ "outbounds" : [{"tag" : "template-marker" , "protocol" : "freedom" , "settings" : {}}],
524+ }
525+ ),
526+ )
527+
528+ first_host_response = client .post (
529+ "/api/host" ,
530+ headers = auth_headers (access_token ),
531+ json = {
532+ "remark" : "Host With Template {USERNAME}" ,
533+ "address" : ["198.51.100.60" ],
534+ "port" : 443 ,
535+ "sni" : ["host-template.example.com" ],
536+ "inbound_tag" : inbound ,
537+ "priority" : 1 ,
538+ "subscription_templates" : {"xray" : override_template ["id" ]},
539+ },
540+ )
541+ assert first_host_response .status_code == status .HTTP_201_CREATED
542+ first_host_id = first_host_response .json ()["id" ]
543+
544+ second_host_response = client .post (
545+ "/api/host" ,
546+ headers = auth_headers (access_token ),
547+ json = {
548+ "remark" : "Host Without Template {USERNAME}" ,
549+ "address" : ["198.51.100.61" ],
550+ "port" : 443 ,
551+ "sni" : ["host-default.example.com" ],
552+ "inbound_tag" : inbound ,
553+ "priority" : 2 ,
554+ },
555+ )
556+ assert second_host_response .status_code == status .HTTP_201_CREATED
557+ second_host_id = second_host_response .json ()["id" ]
558+
559+ group = create_group (access_token , name = unique_name ("xray_isolated_group" ), inbound_tags = [inbound ])
560+ user = create_user (access_token , group_ids = [group ["id" ]], payload = {"username" : unique_name ("xray_isolated_user" )})
561+
562+ try :
563+ response = client .get (f"{ user ['subscription_url' ]} /xray" )
564+ assert response .status_code == status .HTTP_200_OK
565+
566+ configs = response .json ()
567+ assert isinstance (configs , list )
568+ assert len (configs ) == 2
569+
570+ marker_count = 0
571+ for config in configs :
572+ outbounds = config .get ("outbounds" , [])
573+ if any (outbound .get ("tag" ) == "template-marker" for outbound in outbounds ):
574+ marker_count += 1
575+
576+ assert marker_count == 1
577+ finally :
578+ delete_user (access_token , user ["username" ])
579+ delete_group (access_token , group ["id" ])
580+ client .delete (f"/api/host/{ first_host_id } " , headers = auth_headers (access_token ))
581+ client .delete (f"/api/host/{ second_host_id } " , headers = auth_headers (access_token ))
582+ delete_client_template (access_token , override_template ["id" ])
583+ delete_core (access_token , core ["id" ])
584+
585+
512586def test_singbox_subscription_includes_wireguard_outbound (access_token ):
513587 interface_private_key , interface_public_key = generate_wireguard_keypair ()
514588 pre_shared_key , _ = generate_wireguard_keypair ()
0 commit comments