mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d67c4c5848 | |||
| 36b378c561 | |||
| ec763f8105 |
@@ -4,8 +4,10 @@ import userEvent from "@testing-library/user-event";
|
||||
import { afterEach, beforeEach, describe, expect, it, test, vi } from "vitest";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import { PaymentForm } from "#/components/features/payment/payment-form";
|
||||
import * as featureFlags from "#/utils/feature-flags";
|
||||
|
||||
describe("PaymentForm", () => {
|
||||
const billingSettingsSpy = vi.spyOn(featureFlags, "BILLING_SETTINGS");
|
||||
const getBalanceSpy = vi.spyOn(OpenHands, "getBalance");
|
||||
const createCheckoutSessionSpy = vi.spyOn(OpenHands, "createCheckoutSession");
|
||||
const getConfigSpy = vi.spyOn(OpenHands, "getConfig");
|
||||
@@ -26,6 +28,7 @@ describe("PaymentForm", () => {
|
||||
GITHUB_CLIENT_ID: "123",
|
||||
POSTHOG_CLIENT_KEY: "456",
|
||||
});
|
||||
billingSettingsSpy.mockReturnValue(true);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -72,8 +72,10 @@ describe("useTerminal", () => {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
|
||||
expect(mockTerminal.writeln).toHaveBeenNthCalledWith(1, "echo hello");
|
||||
expect(mockTerminal.writeln).toHaveBeenNthCalledWith(2, "hello");
|
||||
// Input commands should be displayed
|
||||
expect(mockTerminal.writeln).toHaveBeenCalledWith("echo hello");
|
||||
// Output commands should be displayed
|
||||
expect(mockTerminal.writeln).toHaveBeenCalledWith("hello");
|
||||
});
|
||||
|
||||
it("should hide secrets in the terminal", () => {
|
||||
@@ -97,13 +99,31 @@ describe("useTerminal", () => {
|
||||
},
|
||||
);
|
||||
|
||||
// BUG: `vi.clearAllMocks()` does not clear the number of calls
|
||||
// therefore, we need to assume the order of the calls based
|
||||
// on the test order
|
||||
expect(mockTerminal.writeln).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
// Input command should be displayed with secrets masked
|
||||
expect(mockTerminal.writeln).toHaveBeenCalledWith(
|
||||
`export GITHUB_TOKEN=${"*".repeat(10)},${"*".repeat(10)},${"*".repeat(10)}`,
|
||||
);
|
||||
expect(mockTerminal.writeln).toHaveBeenNthCalledWith(4, "*".repeat(10));
|
||||
|
||||
// Output command should be displayed with secrets masked
|
||||
expect(mockTerminal.writeln).toHaveBeenCalledWith("*".repeat(10));
|
||||
});
|
||||
|
||||
it("should prevent duplicate command display", () => {
|
||||
const inputCommand = "ls -la";
|
||||
const commands: Command[] = [
|
||||
{ content: inputCommand, type: "input" },
|
||||
{ content: `${inputCommand}\nfile1.txt\nfile2.txt`, type: "output" },
|
||||
];
|
||||
|
||||
render(<TestTerminalComponent commands={commands} secrets={[]} />, {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
|
||||
// Input command should be displayed
|
||||
expect(mockTerminal.writeln).toHaveBeenCalledWith(inputCommand);
|
||||
|
||||
// Output should not be displayed since it starts with the input command
|
||||
// This prevents the duplicate display of the command
|
||||
expect(mockTerminal.writeln).not.toHaveBeenCalledWith(`${inputCommand}\nfile1.txt\nfile2.txt`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useConfig } from "./use-config";
|
||||
import OpenHands from "#/api/open-hands";
|
||||
import { BILLING_SETTINGS } from "#/utils/feature-flags";
|
||||
|
||||
export const useBalance = () => {
|
||||
const { data: config } = useConfig();
|
||||
@@ -8,6 +9,6 @@ export const useBalance = () => {
|
||||
return useQuery({
|
||||
queryKey: ["user", "balance"],
|
||||
queryFn: OpenHands.getBalance,
|
||||
enabled: config?.APP_MODE === "saas",
|
||||
enabled: config?.APP_MODE === "saas" && BILLING_SETTINGS(),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -86,6 +86,8 @@ export const useTerminal = ({
|
||||
|
||||
const handleEnter = (command: string) => {
|
||||
terminal.current?.write("\r\n");
|
||||
// Send the command to the backend but don't echo it back in the terminal
|
||||
// The backend will include the command in its response
|
||||
send(getTerminalCommand(command));
|
||||
};
|
||||
|
||||
@@ -131,10 +133,26 @@ export const useTerminal = ({
|
||||
content = content.replaceAll(secret, "*".repeat(10));
|
||||
});
|
||||
|
||||
terminal.current?.writeln(
|
||||
parseTerminalOutput(content.replaceAll("\n", "\r\n").trim()),
|
||||
);
|
||||
// Check if this is an output that starts with the previous input command
|
||||
// This happens when the backend echoes back the command in the output
|
||||
let shouldDisplayContent = true;
|
||||
if (type === "output" && i > 0 && commands[i - 1].type === "input") {
|
||||
const prevInputCommand = commands[i - 1].content.trim();
|
||||
// If the output starts with the input command, remove it to avoid duplication
|
||||
if (content.trim().startsWith(prevInputCommand)) {
|
||||
// Skip displaying this part as it's a duplicate of the user's input
|
||||
// that's already shown in the terminal
|
||||
shouldDisplayContent = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldDisplayContent) {
|
||||
terminal.current?.writeln(
|
||||
parseTerminalOutput(content.replaceAll("\n", "\r\n").trim()),
|
||||
);
|
||||
}
|
||||
|
||||
// Add a new prompt after the output
|
||||
if (type === "output") {
|
||||
terminal.current.write(`\n$ `);
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class GithubIssueHandler(IssueHandlerInterface):
|
||||
|
||||
def get_headers(self) -> dict[str, str]:
|
||||
return {
|
||||
'Authorization': f'Bearer {self.token}',
|
||||
'Authorization': f'token {self.token}',
|
||||
'Accept': 'application/vnd.github.v3+json',
|
||||
}
|
||||
|
||||
@@ -450,7 +450,7 @@ class GithubPRHandler(GithubIssueHandler):
|
||||
"""Download comments for a specific pull request from Github."""
|
||||
url = f'https://api.github.com/repos/{self.owner}/{self.repo}/issues/{pr_number}/comments'
|
||||
headers = {
|
||||
'Authorization': f'Bearer {self.token}',
|
||||
'Authorization': f'token {self.token}',
|
||||
'Accept': 'application/vnd.github.v3+json',
|
||||
}
|
||||
params = {'per_page': 100, 'page': 1}
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
from openhands.resolver.interfaces.github import GithubIssueHandler, GithubPRHandler
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_github_issue_handler_auth_headers():
|
||||
"""Test that GithubIssueHandler uses Bearer token in authorization headers."""
|
||||
# Create a GithubIssueHandler instance
|
||||
handler = GithubIssueHandler(
|
||||
owner="test-owner",
|
||||
repo="test-repo",
|
||||
token="test-token",
|
||||
username="test-username"
|
||||
)
|
||||
|
||||
# Check that the headers use Bearer token
|
||||
headers = handler.get_headers()
|
||||
assert headers["Authorization"] == "Bearer test-token"
|
||||
|
||||
# Mock requests.get to check headers in API calls
|
||||
with patch("requests.get") as mock_get:
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = []
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
# Call a method that uses requests.get
|
||||
handler.download_issues()
|
||||
|
||||
# Check that the call to requests.get used Bearer token
|
||||
call_args = mock_get.call_args
|
||||
headers_used = call_args[1]["headers"]
|
||||
assert headers_used["Authorization"] == "Bearer test-token"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_github_pr_handler_auth_headers():
|
||||
"""Test that GithubPRHandler uses Bearer token in authorization headers."""
|
||||
# Create a GithubPRHandler instance
|
||||
handler = GithubPRHandler(
|
||||
owner="test-owner",
|
||||
repo="test-repo",
|
||||
token="test-token",
|
||||
username="test-username"
|
||||
)
|
||||
|
||||
# Check that the headers use Bearer token
|
||||
headers = handler.get_headers()
|
||||
assert headers["Authorization"] == "Bearer test-token"
|
||||
|
||||
# Mock requests.post to check headers in GraphQL API calls
|
||||
with patch("requests.post") as mock_post:
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {"data": {"repository": {"pullRequest": {}}}}
|
||||
mock_post.return_value = mock_response
|
||||
|
||||
# Call a method that uses requests.post with GraphQL
|
||||
handler.download_pr_metadata(1)
|
||||
|
||||
# Check that the call to requests.post used Bearer token
|
||||
call_args = mock_post.call_args
|
||||
headers_used = call_args[1]["headers"]
|
||||
assert headers_used["Authorization"] == "Bearer test-token"
|
||||
Reference in New Issue
Block a user