@@ -60,6 +60,102 @@ public function setUp(): void {
6060
6161 }
6262
63+ /**
64+ * Test that the AI Builder URL import rejects local file paths.
65+ */
66+ public function test_ai_builder_file_url_rejects_local_path () {
67+ wp_set_current_user ( $ this ->contibutor_user_id );
68+ $ chart_id = $ this ->factory ->post ->create (
69+ array (
70+ 'post_type ' => Visualizer_Plugin::CPT_VISUALIZER ,
71+ 'post_status ' => 'draft ' ,
72+ 'post_author ' => $ this ->contibutor_user_id ,
73+ )
74+ );
75+ $ file = wp_tempnam ( 'visualizer-local.csv ' );
76+ file_put_contents ( $ file , "Label,Value \nstring,number \nSecret,42 " );
77+
78+ $ _POST = array (
79+ 'chart_id ' => $ chart_id ,
80+ 'nonce ' => wp_create_nonce ( 'visualizer-ai-upload- ' . $ chart_id ),
81+ 'source_type ' => 'file_url ' ,
82+ 'file_url ' => $ file ,
83+ );
84+
85+ try {
86+ $ this ->_handleAjax ( 'visualizer-ai-upload ' );
87+ } catch ( WPAjaxDieContinueException $ e ) {
88+ // Expected after wp_send_json_error().
89+ }
90+ wp_delete_file ( $ file );
91+
92+ $ response = json_decode ( $ this ->_last_response );
93+ $ this ->assertFalse ( $ response ->success );
94+ $ this ->assertSame ( 'Invalid URL. Please check the URL and try again. ' , $ response ->data ->message );
95+ }
96+
97+ /**
98+ * Test that a user cannot request an upload nonce for another user's chart.
99+ */
100+ public function test_ai_builder_chart_nonce_requires_chart_edit_permission () {
101+ $ chart_id = $ this ->factory ->post ->create (
102+ array (
103+ 'post_type ' => Visualizer_Plugin::CPT_VISUALIZER ,
104+ 'post_status ' => 'publish ' ,
105+ 'post_author ' => $ this ->admin_user_id ,
106+ )
107+ );
108+ wp_set_current_user ( $ this ->contibutor_user_id );
109+
110+ $ _POST = array (
111+ 'chart_id ' => $ chart_id ,
112+ 'nonce ' => wp_create_nonce ( 'visualizer-ai-builder ' ),
113+ );
114+
115+ try {
116+ $ this ->_handleAjax ( 'visualizer-ai-chart-nonce ' );
117+ } catch ( WPAjaxDieContinueException $ e ) {
118+ // Expected after wp_send_json_error().
119+ }
120+
121+ $ response = json_decode ( $ this ->_last_response );
122+ $ this ->assertFalse ( $ response ->success );
123+ $ this ->assertSame ( 'Unauthorized. ' , $ response ->data ->message );
124+ }
125+
126+ /**
127+ * Test that a user cannot upload data to another user's chart.
128+ */
129+ public function test_ai_builder_upload_requires_chart_edit_permission () {
130+ $ chart_id = $ this ->factory ->post ->create (
131+ array (
132+ 'post_type ' => Visualizer_Plugin::CPT_VISUALIZER ,
133+ 'post_status ' => 'publish ' ,
134+ 'post_author ' => $ this ->admin_user_id ,
135+ )
136+ );
137+ $ original_content = get_post_field ( 'post_content ' , $ chart_id );
138+ wp_set_current_user ( $ this ->contibutor_user_id );
139+
140+ $ _POST = array (
141+ 'chart_id ' => $ chart_id ,
142+ 'nonce ' => wp_create_nonce ( 'visualizer-ai-upload- ' . $ chart_id ),
143+ 'source_type ' => 'csv_string ' ,
144+ 'csv_data ' => "Label,Value \nstring,number \nSecret,42 " ,
145+ );
146+
147+ try {
148+ $ this ->_handleAjax ( 'visualizer-ai-upload ' );
149+ } catch ( WPAjaxDieContinueException $ e ) {
150+ // Expected after wp_send_json_error().
151+ }
152+
153+ $ response = json_decode ( $ this ->_last_response );
154+ $ this ->assertFalse ( $ response ->success );
155+ $ this ->assertSame ( 'Unauthorized. ' , $ response ->data ->message );
156+ $ this ->assertSame ( $ original_content , get_post_field ( 'post_content ' , $ chart_id ) );
157+ }
158+
63159 /**
64160 * Test the AJAX response for fetching the database data.
65161 */
0 commit comments