Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
A
aisbf
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nexlab
aisbf
Commits
a965a01e
Commit
a965a01e
authored
May 12, 2026
by
Stefy Lanza (nextime / spora )
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: render market references as locked resources
parent
70e0b5e3
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
253 additions
and
8 deletions
+253
-8
providers.py
aisbf/routes/dashboard/providers.py
+38
-0
user_autoselects.html
templates/dashboard/user_autoselects.html
+17
-2
user_providers.html
templates/dashboard/user_providers.html
+21
-1
user_rotations.html
templates/dashboard/user_rotations.html
+17
-2
test_market_reference_imports.py
tests/routes/test_market_reference_imports.py
+160
-3
No files found.
aisbf/routes/dashboard/providers.py
View file @
a965a01e
...
...
@@ -27,6 +27,32 @@ _server_config = None
logger
=
logging
.
getLogger
(
__name__
)
def
_serialize_market_reference
(
reference
:
dict
,
listing
:
dict
|
None
)
->
dict
:
listing
=
listing
or
{}
return
{
'id'
:
f
"market-ref:{reference['id']}"
,
'name'
:
reference
.
get
(
'display_name'
)
or
reference
.
get
(
'source_id'
)
or
reference
.
get
(
'reference_type'
)
or
'Market Reference'
,
'type'
:
reference
.
get
(
'reference_type'
),
'market_reference'
:
True
,
'read_only'
:
True
,
'owner_username'
:
reference
.
get
(
'owner_username'
),
'listing_id'
:
reference
.
get
(
'listing_id'
),
'source_type'
:
reference
.
get
(
'source_type'
),
'source_id'
:
reference
.
get
(
'source_id'
),
'availability'
:
'active'
if
listing
.
get
(
'is_active'
)
else
'unavailable'
,
}
def
_list_dashboard_market_references
(
db
,
user_id
:
int
,
reference_type
:
str
)
->
list
[
dict
]:
references
=
[]
for
reference
in
db
.
list_market_import_references
(
user_id
)
or
[]:
if
reference
.
get
(
'reference_type'
)
!=
reference_type
:
continue
listing
=
db
.
get_market_listing
(
reference
.
get
(
'listing_id'
))
if
reference
.
get
(
'listing_id'
)
else
None
references
.
append
(
_serialize_market_reference
(
reference
,
listing
))
return
references
def
_serialize_provider_usage_snapshot
(
snapshot
):
if
not
snapshot
:
return
None
...
...
@@ -432,6 +458,14 @@ async def dashboard_providers(request: Request):
_ensure_coderai_token
(
provider
[
'config'
]),
broker_status_map
,
)
provider_references
=
_list_dashboard_market_references
(
db
,
current_user_id
,
'provider'
)
for
reference
in
provider_references
:
user_providers
.
append
({
'provider_id'
:
reference
[
'id'
],
'config'
:
reference
,
'created_at'
:
None
,
'updated_at'
:
None
,
})
providers_data
=
user_providers
# Check for success parameter
...
...
@@ -1180,6 +1214,8 @@ async def dashboard_rotations(request: Request):
rotations_data
=
{
"rotations"
:
{},
"notifyerrors"
:
False
}
for
rotation
in
user_rotations
:
rotations_data
[
"rotations"
][
rotation
[
'rotation_id'
]]
=
rotation
[
'config'
]
for
reference
in
_list_dashboard_market_references
(
db
,
current_user_id
,
'rotation'
):
rotations_data
[
"rotations"
][
reference
[
'id'
]]
=
reference
# Get available providers - user-specific for database users
if
is_config_admin
:
...
...
@@ -1404,6 +1440,8 @@ async def dashboard_autoselect(request: Request):
autoselect_data
=
{}
for
autoselect
in
user_autoselects
:
autoselect_data
[
autoselect
[
'autoselect_id'
]]
=
autoselect
[
'config'
]
for
reference
in
_list_dashboard_market_references
(
db
,
current_user_id
,
'autoselect'
):
autoselect_data
[
reference
[
'id'
]]
=
reference
# Check for success parameter
success
=
request
.
query_params
.
get
(
'success'
)
...
...
templates/dashboard/user_autoselects.html
View file @
a965a01e
...
...
@@ -292,6 +292,7 @@ function renderAutoselectList() {
visibleKeys
.
forEach
(
key
=>
{
const
autoselect
=
autoselectConfig
[
key
];
const
isMarketReference
=
!!
(
autoselect
&&
autoselect
.
market_reference
);
const
autoselectItem
=
document
.
createElement
(
'div'
);
autoselectItem
.
className
=
'autoselect-item'
;
autoselectItem
.
dataset
.
sortKey
=
key
;
...
...
@@ -308,10 +309,11 @@ function renderAutoselectList() {
<span style="font-size: 18px;">
${
isExpanded
?
'▼'
:
'▶'
}
</span>
<strong style="font-size: 16px;">
${
escHtml
(
autoselect
.
model_name
||
key
)}
</strong>
<span style="color: var(--color-muted); font-size: 14px;">(
${
modelCount
}
${
modelCount
!==
1
?
window
.
i18n
.
t
(
'autoselect.models_plural'
)
:
window
.
i18n
.
t
(
'autoselect.models_singular'
)}
)</span>
${
isMarketReference
?
'<span class="badge" data-market-reference="true" style="background: var(--color-link); color: white; padding: 2px 8px; border-radius: 999px; font-size: 12px;">Market-linked</span><span class="badge" style="background: var(--bg-accent); color: var(--color-text); padding: 2px 8px; border-radius: 999px; font-size: 12px;">Read-only</span>'
:
''
}
</div>
<div style="display: flex; gap: 8px;">
<button type="button" class="btn btn-secondary" onclick="event.stopPropagation(); copyAutoselect('
${
safeKey
}
')" style="padding: 5px 15px;">
${
window
.
i18n
.
t
(
'autoselect.copy'
)}
</button>
<button type="button" class="btn btn-secondary" onclick="event.stopPropagation(); removeAutoselect('
${
safeKey
}
')" style="background: #dc3545; padding: 5px 15px;">
${
window
.
i18n
.
t
(
'autoselect.remove'
)}
</button>
${
isMarketReference
?
''
:
`
<button type="button" class="btn btn-secondary" onclick="event.stopPropagation(); copyAutoselect('
${
safeKey
}
')" style="padding: 5px 15px;">
${
window
.
i18n
.
t
(
'autoselect.copy'
)}
</button>
<button type="button" class="btn btn-secondary" onclick="event.stopPropagation(); removeAutoselect('
${
safeKey
}
')" style="background: #dc3545; padding: 5px 15px;">
${
window
.
i18n
.
t
(
'autoselect.remove'
)}
</button>
`
}
</div>
</div>
<div id="autoselect-details-
${
escHtmlAttr
(
key
)}
" style="display:
${
isExpanded
?
'block'
:
'none'
}
; padding: 20px; border-top: 1px solid var(--color-border); background: var(--bg-panel);">
...
...
@@ -357,6 +359,19 @@ function toggleAutoselect(key) {
function
renderAutoselectDetails
(
autoselectKey
)
{
const
container
=
document
.
getElementById
(
`autoselect-details-
${
autoselectKey
}
`
);
const
autoselect
=
autoselectConfig
[
autoselectKey
];
if
(
autoselect
&&
autoselect
.
market_reference
)
{
container
.
innerHTML
=
`
<div data-market-reference="true" style="border: 1px solid var(--color-border); border-radius: 6px; padding: 16px; background: var(--bg-page);">
<div style="display:flex; gap:8px; flex-wrap:wrap; margin-bottom:10px;">
<span class="badge" data-market-reference="true" style="background: var(--color-link); color: white; padding: 2px 8px; border-radius: 999px; font-size: 12px;">Market-linked</span>
<span class="badge" style="background: var(--bg-accent); color: var(--color-text); padding: 2px 8px; border-radius: 999px; font-size: 12px;">Read-only</span>
</div>
<p style="margin:0 0 8px 0;"><strong>Source:</strong>
${
escHtml
(
autoselect
.
owner_username
||
'Unknown'
)}
/
${
escHtml
(
autoselect
.
source_id
||
''
)}
</p>
<p style="margin:0;"><strong>Availability:</strong>
${
escHtml
(
autoselect
.
availability
||
'unavailable'
)}
</p>
</div>
`
;
return
;
}
const
safeAKey
=
autoselectKey
.
replace
(
/
\\
/g
,
'
\\\
\'
).replace(/'
/
g
,
"
\\
'"
);
const
inheritedCaps
=
Array
.
isArray
(
autoselect
.
capabilities
)
?
autoselect
.
capabilities
:
[];
const
partialCaps
=
Array
.
isArray
(
autoselect
.
partial_capabilities
)
?
autoselect
.
partial_capabilities
:
[];
...
...
templates/dashboard/user_providers.html
View file @
a965a01e
...
...
@@ -340,6 +340,11 @@ function _providerSupportsUsage(key) {
return p
&&
p.type === 'codex';
}
function isMarketReferenceProvider(key) {
const provider = providersData[key];
return !!(provider
&&
provider.market_reference);
}
function _fmtSeconds(s) {
if (!s || s
<
=
0
)
return
'
0s
';
const
h =
Math.floor(s/3600),
m =
Math.floor((s%3600)/60),
sec =
s%60;
...
...
@@ -653,6 +658,7 @@ function renderProvidersList() {
} else {
pageKeys.forEach(key => {
const provider = providersData[key];
const isMarketReference = isMarketReferenceProvider(key);
const providerItem = document.createElement('div');
providerItem.className = 'provider-item';
providerItem.dataset.sortKey = key;
...
...
@@ -672,9 +678,10 @@ function renderProvidersList() {
<span
style=
"font-size: 18px;"
>
${isExpanded ? '▼' : '▶'}
</span>
<strong
style=
"font-size: 16px;"
>
${escHtmlAttr(key)}
</strong>
<span
style=
"color: var(--color-muted); font-size: 14px;"
>
(${escHtmlAttr(provider.name || key)})
</span>
${isMarketReference ? '
<span
class=
"badge"
data-market-reference=
"true"
style=
"background: var(--color-link); color: white; padding: 2px 8px; border-radius: 999px; font-size: 12px;"
>
Market-linked
</span><span
class=
"badge"
style=
"background: var(--bg-accent); color: var(--color-text); padding: 2px 8px; border-radius: 999px; font-size: 12px;"
>
Read-only
</span>
' : ''}
${compactBadge}
</div>
<button
type=
"button"
class=
"btn btn-secondary"
onclick=
"event.stopPropagation(); removeProvider('${safeKey}')"
style=
"background: #dc3545; padding: 5px 15px;"
>
${window.i18n.t('providers.remove')}
</button>
${isMarketReference ? '' : `
<button
type=
"button"
class=
"btn btn-secondary"
onclick=
"event.stopPropagation(); removeProvider('${safeKey}')"
style=
"background: #dc3545; padding: 5px 15px;"
>
${window.i18n.t('providers.remove')}
</button>
`}
</div>
<div
id=
"provider-details-${escHtmlAttr(key)}"
style=
"display: ${isExpanded ? 'block' : 'none'}; padding: 20px; border-top: 1px solid var(--color-border); background: var(--bg-panel);"
>
</div>
...
...
@@ -762,6 +769,19 @@ function toggleProvider(key) {
function renderProviderDetails(key) {
const container = document.getElementById(`provider-details-${key}`);
const provider = providersData[key];
if (provider
&&
provider.market_reference) {
container.innerHTML = `
<div
data-market-reference=
"true"
style=
"border: 1px solid var(--color-border); border-radius: 6px; padding: 16px; background: var(--bg-page);"
>
<div
style=
"display:flex; gap:8px; flex-wrap:wrap; margin-bottom:10px;"
>
<span
class=
"badge"
data-market-reference=
"true"
style=
"background: var(--color-link); color: white; padding: 2px 8px; border-radius: 999px; font-size: 12px;"
>
Market-linked
</span>
<span
class=
"badge"
style=
"background: var(--bg-accent); color: var(--color-text); padding: 2px 8px; border-radius: 999px; font-size: 12px;"
>
Read-only
</span>
</div>
<p
style=
"margin:0 0 8px 0;"
><strong>
Source:
</strong>
${escHtmlAttr(provider.owner_username || 'Unknown')} / ${escHtmlAttr(provider.source_id || '')}
</p>
<p
style=
"margin:0;"
><strong>
Availability:
</strong>
${escHtmlAttr(provider.availability || 'unavailable')}
</p>
</div>
`;
return;
}
const safeKey = key.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
const isKiroProvider = provider.type === 'kiro';
const isClaudeProvider = provider.type === 'claude';
...
...
templates/dashboard/user_rotations.html
View file @
a965a01e
...
...
@@ -273,6 +273,7 @@ function renderRotationsList() {
rotationMasterOrder
.
filter
(
k
=>
k
in
(
rotationsConfig
.
rotations
||
{})).
forEach
(
key
=>
{
const
rotation
=
rotationsConfig
.
rotations
[
key
];
const
isMarketReference
=
!!
(
rotation
&&
rotation
.
market_reference
);
const
rotationItem
=
document
.
createElement
(
'div'
);
rotationItem
.
className
=
'rotation-item'
;
rotationItem
.
dataset
.
sortKey
=
key
;
...
...
@@ -289,10 +290,11 @@ function renderRotationsList() {
<span style="font-size: 18px;">
${
isExpanded
?
'▼'
:
'▶'
}
</span>
<strong style="font-size: 16px;">
${
escHtmlAttr
(
key
)}
</strong>
<span style="color: var(--color-muted); font-size: 14px;">(
${
providerCount
}
${
window
.
i18n
.
t
(
providerCount
!==
1
?
'rotations.providers_plural'
:
'rotations.providers_singular'
)}
)</span>
${
isMarketReference
?
'<span class="badge" data-market-reference="true" style="background: var(--color-link); color: white; padding: 2px 8px; border-radius: 999px; font-size: 12px;">Market-linked</span><span class="badge" style="background: var(--bg-accent); color: var(--color-text); padding: 2px 8px; border-radius: 999px; font-size: 12px;">Read-only</span>'
:
''
}
</div>
<div style="display: flex; gap: 8px;">
<button type="button" class="btn btn-secondary" onclick="event.stopPropagation(); copyRotation('
${
safeKey
}
')" style="padding: 5px 15px;">
${
window
.
i18n
.
t
(
'rotations.copy'
)}
</button>
<button type="button" class="btn btn-secondary" onclick="event.stopPropagation(); removeRotation('
${
safeKey
}
')" style="background: #dc3545; padding: 5px 15px;">
${
window
.
i18n
.
t
(
'rotations.remove'
)}
</button>
${
isMarketReference
?
''
:
`
<button type="button" class="btn btn-secondary" onclick="event.stopPropagation(); copyRotation('
${
safeKey
}
')" style="padding: 5px 15px;">
${
window
.
i18n
.
t
(
'rotations.copy'
)}
</button>
<button type="button" class="btn btn-secondary" onclick="event.stopPropagation(); removeRotation('
${
safeKey
}
')" style="background: #dc3545; padding: 5px 15px;">
${
window
.
i18n
.
t
(
'rotations.remove'
)}
</button>
`
}
</div>
</div>
<div id="rotation-details-
${
escHtmlAttr
(
key
)}
" style="display:
${
isExpanded
?
'block'
:
'none'
}
; padding: 20px; border-top: 1px solid var(--color-border); background: var(--bg-panel);">
...
...
@@ -337,6 +339,19 @@ function toggleRotation(key) {
function
renderRotationDetails
(
rotationKey
)
{
const
container
=
document
.
getElementById
(
`rotation-details-
${
rotationKey
}
`
);
const
rotation
=
rotationsConfig
.
rotations
[
rotationKey
];
if
(
rotation
&&
rotation
.
market_reference
)
{
container
.
innerHTML
=
`
<div data-market-reference="true" style="border: 1px solid var(--color-border); border-radius: 6px; padding: 16px; background: var(--bg-page);">
<div style="display:flex; gap:8px; flex-wrap:wrap; margin-bottom:10px;">
<span class="badge" data-market-reference="true" style="background: var(--color-link); color: white; padding: 2px 8px; border-radius: 999px; font-size: 12px;">Market-linked</span>
<span class="badge" style="background: var(--bg-accent); color: var(--color-text); padding: 2px 8px; border-radius: 999px; font-size: 12px;">Read-only</span>
</div>
<p style="margin:0 0 8px 0;"><strong>Source:</strong>
${
escHtmlAttr
(
rotation
.
owner_username
||
'Unknown'
)}
/
${
escHtmlAttr
(
rotation
.
source_id
||
''
)}
</p>
<p style="margin:0;"><strong>Availability:</strong>
${
escHtmlAttr
(
rotation
.
availability
||
'unavailable'
)}
</p>
</div>
`
;
return
;
}
const
safeRKey
=
rotationKey
.
replace
(
/
\\
/g
,
'
\\\
\'
).replace(/'
/
g
,
"
\\
'"
);
const
inheritedCaps
=
Array
.
isArray
(
rotation
.
capabilities
)
?
rotation
.
capabilities
:
[];
const
partialCaps
=
Array
.
isArray
(
rotation
.
partial_capabilities
)
?
rotation
.
partial_capabilities
:
[];
...
...
tests/routes/test_market_reference_imports.py
View file @
a965a01e
...
...
@@ -46,6 +46,9 @@ class MarketReferenceImportDbStub:
self
.
recorded_imports
=
[]
self
.
created_references
=
[]
self
.
reference_rows
=
[]
self
.
user_providers
=
[]
self
.
user_rotations
=
[]
self
.
user_autoselects
=
[]
self
.
listing
=
{
"id"
:
55
,
"owner_user_id"
:
7
,
...
...
@@ -196,6 +199,12 @@ class MarketReferenceImportDbStub:
return
dict
(
row
)
return
None
def
list_market_import_references
(
self
,
user_id
):
return
[
dict
(
row
)
for
row
in
self
.
reference_rows
if
row
[
"user_id"
]
==
user_id
]
def
get_sort_order
(
self
,
user_id
,
resource_type
):
return
None
def
save_user_provider
(
self
,
user_id
,
provider_name
,
config
):
self
.
saved_user_providers
.
append
((
user_id
,
provider_name
,
config
))
...
...
@@ -217,13 +226,13 @@ class MarketReferenceImportDbStub:
return
len
(
self
.
recorded_imports
)
def
get_user_providers
(
self
,
user_id
):
return
[]
return
[
dict
(
row
)
for
row
in
self
.
user_providers
]
def
get_user_rotations
(
self
,
user_id
):
return
[]
return
[
dict
(
row
)
for
row
in
self
.
user_rotations
]
def
get_user_autoselects
(
self
,
user_id
):
return
[]
return
[
dict
(
row
)
for
row
in
self
.
user_autoselects
]
class
RegistryStub
:
...
...
@@ -263,6 +272,154 @@ def _login_as_user(client: TestClient, user_id: int = 11) -> None:
)
def
_seed_dashboard_market_reference_mix
(
db
:
MarketReferenceImportDbStub
)
->
None
:
provider_reference
=
{
"id"
:
1
,
"user_id"
:
11
,
"listing_id"
:
55
,
"reference_type"
:
"provider"
,
"display_name"
:
"Alice Provider"
,
"owner_username"
:
"alice"
,
"source_type"
:
"provider"
,
"source_id"
:
"alice-provider"
,
"is_active"
:
True
,
}
rotation_reference
=
{
"id"
:
2
,
"user_id"
:
11
,
"listing_id"
:
56
,
"reference_type"
:
"rotation"
,
"display_name"
:
"Alice Rotation"
,
"owner_username"
:
"alice"
,
"source_type"
:
"rotation"
,
"source_id"
:
"alice-rotation"
,
"is_active"
:
True
,
}
autoselect_reference
=
{
"id"
:
3
,
"user_id"
:
11
,
"listing_id"
:
57
,
"reference_type"
:
"autoselect"
,
"display_name"
:
"Alice Autoselect"
,
"owner_username"
:
"alice"
,
"source_type"
:
"autoselect"
,
"source_id"
:
"alice-autoselect"
,
"is_active"
:
True
,
}
db
.
reference_rows
=
[
provider_reference
,
rotation_reference
,
autoselect_reference
]
db
.
user_providers
=
[
{
"provider_id"
:
"local-provider"
,
"config"
:
{
"name"
:
"Local Provider"
,
"type"
:
"openai"
,
"models"
:
[]},
"created_at"
:
None
,
"updated_at"
:
None
,
}
]
db
.
user_rotations
=
[
{
"rotation_id"
:
"local-rotation"
,
"config"
:
{
"model_name"
:
"Local Rotation"
,
"providers"
:
[]},
}
]
db
.
user_autoselects
=
[
{
"autoselect_id"
:
"local-autoselect"
,
"config"
:
{
"model_name"
:
"Local Autoselect"
,
"description"
:
"Local chooser"
,
"selection_model"
:
"internal"
,
"fallback"
:
""
,
"available_models"
:
[],
},
}
]
def
test_dashboard_providers_renders_market_reference_alongside_local_provider
(
monkeypatch
):
db
=
MarketReferenceImportDbStub
()
_seed_dashboard_market_reference_mix
(
db
)
capture
=
TemplateCapture
()
client
=
TestClient
(
app
)
_login_as_user
(
client
)
monkeypatch
.
setattr
(
dashboard_market
,
"DatabaseRegistry"
,
RegistryStub
(
db
))
from
aisbf.routes.dashboard
import
providers
as
dashboard_providers
monkeypatch
.
setattr
(
dashboard_providers
,
"DatabaseRegistry"
,
RegistryStub
(
db
))
monkeypatch
.
setattr
(
dashboard_providers
,
"_templates"
,
capture
)
response
=
client
.
get
(
"/dashboard/providers"
)
assert
response
.
status_code
==
200
assert
"Local Provider"
in
response
.
text
assert
"Alice Provider"
in
response
.
text
assert
"Market-linked"
in
response
.
text
assert
"Read-only"
in
response
.
text
def
test_market_references_do_not_render_local_edit_controls
(
monkeypatch
):
db
=
MarketReferenceImportDbStub
()
_seed_dashboard_market_reference_mix
(
db
)
capture
=
TemplateCapture
()
client
=
TestClient
(
app
)
_login_as_user
(
client
)
monkeypatch
.
setattr
(
dashboard_market
,
"DatabaseRegistry"
,
RegistryStub
(
db
))
from
aisbf.routes.dashboard
import
providers
as
dashboard_providers
monkeypatch
.
setattr
(
dashboard_providers
,
"DatabaseRegistry"
,
RegistryStub
(
db
))
monkeypatch
.
setattr
(
dashboard_providers
,
"_templates"
,
capture
)
response
=
client
.
get
(
"/dashboard/providers"
)
assert
response
.
status_code
==
200
assert
'data-market-reference="true"'
in
response
.
text
assert
'removeProvider(
\'
market-ref:1
\'
)'
not
in
response
.
text
assert
'Edit Market Reference'
not
in
response
.
text
def
test_dashboard_rotations_renders_market_reference_alongside_local_rotation
(
monkeypatch
):
db
=
MarketReferenceImportDbStub
()
_seed_dashboard_market_reference_mix
(
db
)
capture
=
TemplateCapture
()
client
=
TestClient
(
app
)
_login_as_user
(
client
)
monkeypatch
.
setattr
(
dashboard_market
,
"DatabaseRegistry"
,
RegistryStub
(
db
))
from
aisbf.routes.dashboard
import
providers
as
dashboard_providers
monkeypatch
.
setattr
(
dashboard_providers
,
"DatabaseRegistry"
,
RegistryStub
(
db
))
monkeypatch
.
setattr
(
dashboard_providers
,
"_templates"
,
capture
)
response
=
client
.
get
(
"/dashboard/rotations"
)
assert
response
.
status_code
==
200
assert
"Local Rotation"
in
response
.
text
assert
"Alice Rotation"
in
response
.
text
assert
"Market-linked"
in
response
.
text
assert
"Read-only"
in
response
.
text
assert
'copyRotation(
\'
market-ref:2
\'
)'
not
in
response
.
text
def
test_dashboard_autoselect_renders_market_reference_alongside_local_entry
(
monkeypatch
):
db
=
MarketReferenceImportDbStub
()
_seed_dashboard_market_reference_mix
(
db
)
capture
=
TemplateCapture
()
client
=
TestClient
(
app
)
_login_as_user
(
client
)
monkeypatch
.
setattr
(
dashboard_market
,
"DatabaseRegistry"
,
RegistryStub
(
db
))
from
aisbf.routes.dashboard
import
providers
as
dashboard_providers
monkeypatch
.
setattr
(
dashboard_providers
,
"DatabaseRegistry"
,
RegistryStub
(
db
))
monkeypatch
.
setattr
(
dashboard_providers
,
"_templates"
,
capture
)
response
=
client
.
get
(
"/dashboard/autoselect"
)
assert
response
.
status_code
==
200
assert
"Local Autoselect"
in
response
.
text
assert
"Alice Autoselect"
in
response
.
text
assert
"Market-linked"
in
response
.
text
assert
"Read-only"
in
response
.
text
assert
'copyAutoselect(
\'
market-ref:3
\'
)'
not
in
response
.
text
def
test_import_market_listing_creates_market_reference_for_provider
(
monkeypatch
):
db
=
MarketReferenceImportDbStub
()
client
=
TestClient
(
app
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment