|
24 | 24 |
|
25 | 25 | import pytest |
26 | 26 |
|
| 27 | +from codeflash.context.code_context_extractor import get_code_optimization_context_for_language |
| 28 | +from codeflash.discovery.functions_to_optimize import FunctionToOptimize |
27 | 29 | from codeflash.languages.base import Language |
28 | 30 | from codeflash.languages.javascript.support import JavaScriptSupport, TypeScriptSupport |
29 | 31 |
|
@@ -1694,6 +1696,240 @@ def test_function_with_computed_property_names(self, js_support, temp_project): |
1694 | 1696 | };""" |
1695 | 1697 | assert context.read_only_context == expected_read_only |
1696 | 1698 |
|
| 1699 | + def test_with_tricky_helpers(self, ts_support, temp_project): |
| 1700 | + """Test function returning object with computed property names.""" |
| 1701 | + code = """import { WebClient, ChatPostMessageArguments } from "@slack/web-api" |
| 1702 | +
|
| 1703 | +// Dependencies interface for easier testing |
| 1704 | +export interface SendSlackMessageDependencies { |
| 1705 | + WebClient: typeof WebClient |
| 1706 | + getSlackToken: () => string | undefined |
| 1707 | + getSlackChannelId: () => string | undefined |
| 1708 | + console: typeof console |
| 1709 | +} |
| 1710 | +
|
| 1711 | +// Default dependencies |
| 1712 | +let dependencies: SendSlackMessageDependencies = { |
| 1713 | + WebClient, |
| 1714 | + getSlackToken: () => process.env.SLACK_TOKEN, |
| 1715 | + getSlackChannelId: () => process.env.SLACK_CHANNEL_ID, |
| 1716 | + console, |
| 1717 | +} |
| 1718 | +
|
| 1719 | +// For testing - allow dependency injection |
| 1720 | +export function setSendSlackMessageDependencies(deps: Partial<SendSlackMessageDependencies>) { |
| 1721 | + dependencies = { ...dependencies, ...deps } |
| 1722 | +} |
| 1723 | +
|
| 1724 | +export function resetSendSlackMessageDependencies() { |
| 1725 | + dependencies = { |
| 1726 | + WebClient, |
| 1727 | + getSlackToken: () => process.env.SLACK_TOKEN, |
| 1728 | + getSlackChannelId: () => process.env.SLACK_CHANNEL_ID, |
| 1729 | + console, |
| 1730 | + } |
| 1731 | +} |
| 1732 | +
|
| 1733 | +// Initialize web client |
| 1734 | +let web: WebClient | null = null |
| 1735 | +
|
| 1736 | +export function initializeWebClient() { |
| 1737 | + const SLACK_TOKEN = dependencies.getSlackToken() |
| 1738 | + const SLACK_CHANNEL_ID = dependencies.getSlackChannelId() |
| 1739 | +
|
| 1740 | + if (!SLACK_TOKEN) { |
| 1741 | + throw new Error("Missing SLACK_TOKEN") |
| 1742 | + } |
| 1743 | +
|
| 1744 | + if (!SLACK_CHANNEL_ID) { |
| 1745 | + throw new Error("Missing SLACK_CHANNEL_ID") |
| 1746 | + } |
| 1747 | +
|
| 1748 | + if (!web) { |
| 1749 | + web = new dependencies.WebClient(SLACK_TOKEN, {}) |
| 1750 | + } |
| 1751 | +
|
| 1752 | + return web |
| 1753 | +} |
| 1754 | +
|
| 1755 | +// For testing - allow resetting the web client |
| 1756 | +export function resetWebClient() { |
| 1757 | + web = null |
| 1758 | +} |
| 1759 | +
|
| 1760 | +/** |
| 1761 | + * Send a message to Slack |
| 1762 | + * |
| 1763 | + * @param {string|object} message - Text message or Block Kit message object |
| 1764 | + * @param {string|null} channel - Channel ID, defaults to SLACK_CHANNEL_ID |
| 1765 | + * @param {boolean} returnData - Whether to return the full Slack API response |
| 1766 | + * @returns {Promise<boolean|object>} - True or API response |
| 1767 | + */ |
| 1768 | +export const sendSlackMessage = async ( |
| 1769 | + message: any, |
| 1770 | + channel: string | null = null, |
| 1771 | + returnData: boolean = false, |
| 1772 | +): Promise<boolean | object> => { |
| 1773 | + return new Promise(async (resolve, reject) => { |
| 1774 | + try { |
| 1775 | + const webClient = initializeWebClient() |
| 1776 | + const SLACK_CHANNEL_ID = dependencies.getSlackChannelId() |
| 1777 | + const channelId = channel || SLACK_CHANNEL_ID |
| 1778 | +
|
| 1779 | + // Configure the message payload depending on the input type |
| 1780 | + let payload: ChatPostMessageArguments |
| 1781 | +
|
| 1782 | + if (typeof message === "string") { |
| 1783 | + payload = { |
| 1784 | + channel: channelId, |
| 1785 | + text: message, |
| 1786 | + } |
| 1787 | + } else if (message && typeof message === "object") { |
| 1788 | + if (message.blocks) { |
| 1789 | + payload = { |
| 1790 | + channel: channelId, |
| 1791 | + text: message.text || "Notification from CodeFlash", |
| 1792 | + blocks: message.blocks, |
| 1793 | + } |
| 1794 | + } else { |
| 1795 | + dependencies.console.warn("Object passed to sendSlackMessage without blocks property") |
| 1796 | + payload = { |
| 1797 | + channel: channelId, |
| 1798 | + text: JSON.stringify(message), |
| 1799 | + } |
| 1800 | + } |
| 1801 | + } else { |
| 1802 | + dependencies.console.error("Invalid message type", typeof message) |
| 1803 | + payload = { |
| 1804 | + channel: channelId, |
| 1805 | + text: "Invalid message", |
| 1806 | + } |
| 1807 | + } |
| 1808 | +
|
| 1809 | + // console.log("Sending payload to Slack:", JSON.stringify(payload, null, 2)); |
| 1810 | +
|
| 1811 | + const resp = await webClient.chat.postMessage(payload) |
| 1812 | + return resolve(returnData ? resp : true) |
| 1813 | + } catch (error) { |
| 1814 | + dependencies.console.error("Error sending Slack message:", error) |
| 1815 | + return resolve(returnData ? { error } : true) |
| 1816 | + } |
| 1817 | + }) |
| 1818 | +} |
| 1819 | +""" |
| 1820 | + file_path = temp_project / "slack_util.ts" |
| 1821 | + file_path.write_text(code, encoding="utf-8") |
| 1822 | + target_func = "sendSlackMessage" |
| 1823 | + |
| 1824 | + functions = ts_support.discover_functions(file_path) |
| 1825 | + func_info = next(f for f in functions if f.name == target_func) |
| 1826 | + fto = FunctionToOptimize( |
| 1827 | + function_name=target_func, |
| 1828 | + file_path=file_path, |
| 1829 | + parents=func_info.parents, |
| 1830 | + starting_line=func_info.start_line, |
| 1831 | + ending_line=func_info.end_line, |
| 1832 | + starting_col=func_info.start_col, |
| 1833 | + ending_col=func_info.end_col, |
| 1834 | + is_async=func_info.is_async, |
| 1835 | + language="typescript", |
| 1836 | + ) |
| 1837 | + |
| 1838 | + ctx = get_code_optimization_context_for_language( |
| 1839 | + fto, temp_project |
| 1840 | + ) |
| 1841 | + |
| 1842 | + # The read_writable_code should contain the target function AND helper functions |
| 1843 | + expected_read_writable = """```typescript:slack_util.ts |
| 1844 | +import { WebClient, ChatPostMessageArguments } from "@slack/web-api" |
| 1845 | +
|
| 1846 | +export const sendSlackMessage = async ( |
| 1847 | + message: any, |
| 1848 | + channel: string | null = null, |
| 1849 | + returnData: boolean = false, |
| 1850 | +): Promise<boolean | object> => { |
| 1851 | + return new Promise(async (resolve, reject) => { |
| 1852 | + try { |
| 1853 | + const webClient = initializeWebClient() |
| 1854 | + const SLACK_CHANNEL_ID = dependencies.getSlackChannelId() |
| 1855 | + const channelId = channel || SLACK_CHANNEL_ID |
| 1856 | +
|
| 1857 | + // Configure the message payload depending on the input type |
| 1858 | + let payload: ChatPostMessageArguments |
| 1859 | +
|
| 1860 | + if (typeof message === "string") { |
| 1861 | + payload = { |
| 1862 | + channel: channelId, |
| 1863 | + text: message, |
| 1864 | + } |
| 1865 | + } else if (message && typeof message === "object") { |
| 1866 | + if (message.blocks) { |
| 1867 | + payload = { |
| 1868 | + channel: channelId, |
| 1869 | + text: message.text || "Notification from CodeFlash", |
| 1870 | + blocks: message.blocks, |
| 1871 | + } |
| 1872 | + } else { |
| 1873 | + dependencies.console.warn("Object passed to sendSlackMessage without blocks property") |
| 1874 | + payload = { |
| 1875 | + channel: channelId, |
| 1876 | + text: JSON.stringify(message), |
| 1877 | + } |
| 1878 | + } |
| 1879 | + } else { |
| 1880 | + dependencies.console.error("Invalid message type", typeof message) |
| 1881 | + payload = { |
| 1882 | + channel: channelId, |
| 1883 | + text: "Invalid message", |
| 1884 | + } |
| 1885 | + } |
| 1886 | +
|
| 1887 | + // console.log("Sending payload to Slack:", JSON.stringify(payload, null, 2)); |
| 1888 | +
|
| 1889 | + const resp = await webClient.chat.postMessage(payload) |
| 1890 | + return resolve(returnData ? resp : true) |
| 1891 | + } catch (error) { |
| 1892 | + dependencies.console.error("Error sending Slack message:", error) |
| 1893 | + return resolve(returnData ? { error } : true) |
| 1894 | + } |
| 1895 | + }) |
| 1896 | +} |
| 1897 | +
|
| 1898 | +
|
| 1899 | +export function initializeWebClient() { |
| 1900 | + const SLACK_TOKEN = dependencies.getSlackToken() |
| 1901 | + const SLACK_CHANNEL_ID = dependencies.getSlackChannelId() |
| 1902 | +
|
| 1903 | + if (!SLACK_TOKEN) { |
| 1904 | + throw new Error("Missing SLACK_TOKEN") |
| 1905 | + } |
| 1906 | +
|
| 1907 | + if (!SLACK_CHANNEL_ID) { |
| 1908 | + throw new Error("Missing SLACK_CHANNEL_ID") |
| 1909 | + } |
| 1910 | +
|
| 1911 | + if (!web) { |
| 1912 | + web = new dependencies.WebClient(SLACK_TOKEN, {}) |
| 1913 | + } |
| 1914 | +
|
| 1915 | + return web |
| 1916 | +} |
| 1917 | +```""" |
| 1918 | + |
| 1919 | + # The read_only_context should contain global variables (dependencies object, web client) |
| 1920 | + # but NOT have invalid floating object properties |
| 1921 | + expected_read_only = """let dependencies: SendSlackMessageDependencies = { |
| 1922 | + WebClient, |
| 1923 | + getSlackToken: () => process.env.SLACK_TOKEN, |
| 1924 | + getSlackChannelId: () => process.env.SLACK_CHANNEL_ID, |
| 1925 | + console, |
| 1926 | +} |
| 1927 | +let web: WebClient | null = null""" |
| 1928 | + |
| 1929 | + assert ctx.read_writable_code.markdown == expected_read_writable |
| 1930 | + assert ctx.read_only_context_code == expected_read_only |
| 1931 | + |
| 1932 | + |
1697 | 1933 |
|
1698 | 1934 | class TestContextProperties: |
1699 | 1935 | """Tests for CodeContext object properties.""" |
|
0 commit comments