FIX: Ensure that the current user's guardian is used when running AI tools tests. (#38676)

The tool runner guardian should be the same as the current user running
the tests.

Co-authored-by: discourse-patch-triage[bot] <272280883+discourse-patch-triage[bot]@users.noreply.github.com>
This commit is contained in:
Gabriel Grubba
2026-04-29 16:25:52 -03:00
committed by GitHub
co-authored by discourse-patch-triage[bot] <272280883+discourse-patch-triage[bot]@users.noreply.github.com>
parent 2a37362118
commit 6c10d900f3
4 changed files with 70 additions and 2 deletions
@@ -115,11 +115,13 @@ module DiscourseAi
# we need an llm so we have a tokenizer
# but will do without if none is available
llm = LlmModel.first&.to_llm
context = DiscourseAi::Agents::BotContext.new(guardian: guardian)
runner =
@ai_tool.runner(
parameters,
llm: llm,
bot_user: current_user,
context: context,
secret_bindings: test_bindings,
)
result = runner.invoke
@@ -24,7 +24,8 @@ module DiscourseAi
:temporal_context,
:user_language,
:bypass_response_format,
:mcp_state
:mcp_state,
:guardian
def initialize(
post: nil,
@@ -47,7 +48,8 @@ module DiscourseAi
cancel_manager: nil,
inferred_concepts: [],
format_dates: false,
bypass_response_format: false
bypass_response_format: false,
guardian: nil
)
@participants = participants
@user = user
@@ -75,6 +77,8 @@ module DiscourseAi
@bypass_response_format = bypass_response_format
@mcp_state = {}
@guardian = guardian
if post
@post_id = post.id
@topic_id = post.topic_id
@@ -67,6 +67,39 @@ module DiscourseAi
@system_guardian ||= Guardian.new(::Discourse.system_user)
end
def resolve_user(username)
if username.present?
User.find_by(username: username)
else
@bot_user || ::Discourse.system_user
end
end
def resolve_guardian(username)
user = resolve_user(username)
return nil, nil if user.nil?
guardian =
if user.staged?
@context.guardian || system_guardian
else
Guardian.new(user)
end
[user, guardian]
end
def resolve_category(category_id_or_name)
if category_id_or_name.is_a?(Integer) ||
category_id_or_name.to_i.to_s == category_id_or_name.to_s
Category.find_by(id: category_id_or_name.to_i)
else
Category
.where(name: category_id_or_name)
.or(Category.where(slug: category_id_or_name))
.first
end
end
def mini_racer_context
@mini_racer_context ||=
begin
@@ -544,5 +544,34 @@ RSpec.describe DiscourseAi::Admin::AiToolsController do
expect(response.status).to eq(200)
expect(response.parsed_body["output"]["key"]).to eq(ai_secret.secret)
end
it "passes the admin user's guardian via context for staged user discourse operations" do
category = Fabricate(:category)
staged_user = Fabricate(:user, staged: true)
guardians_used = []
original_pc = PostCreator.instance_method(:initialize)
PostCreator.define_method(:initialize) do |user, opts = {}|
guardians_used << opts[:guardian] if opts[:guardian]
original_pc.bind(self).call(user, opts)
end
post "/admin/plugins/discourse-ai/ai-tools/#{ai_tool.id}/test.json",
params: {
ai_tool: {
script:
"function invoke(params) { return discourse.createTopic({ title: 'Test topic from tool test action', raw: 'This is a test body for the topic created during tool testing', category_id: #{category.id}, username: '#{staged_user.username}' }); }",
},
parameters: {
input: "test",
},
}
expect(response.status).to eq(200)
expect(response.parsed_body["output"]["success"]).to eq(true)
expect(guardians_used).not_to be_empty
expect(guardians_used.last.user.id).to eq(admin.id)
ensure
PostCreator.define_method(:initialize, original_pc)
end
end
end