Changeset 2
- Timestamp:
- 08/15/08 22:48:13 (4 years ago)
- Files:
-
- 1 modified
-
trunk/epic/epic.sql (modified) (15 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/epic/epic.sql
r1 r2 63 63 -- module: test_users 64 64 DECLARE 65 v_user_id integer;66 v_user_rec users%ROWTYPE;65 user_id integer; 66 user_rec users%ROWTYPE; 67 67 BEGIN 68 68 <<MAIN>> 69 69 BEGIN 70 70 -- Create dummy records 71 INSERT INTO users (login_name) VALUES ('test1') RETURNING user_id INTO v_user_id;71 INSERT INTO users (login_name) VALUES ('test1') RETURNING user_id INTO user_id; 72 72 73 73 -- Run the proc 74 PERFORM "inner".set_user_state( v_user_id);74 PERFORM "inner".set_user_state(user_id); 75 75 76 76 -- The proc MUST set users.state to 'active'; 77 SELECT INTO v_user_rec * FROM users WHERE user_id = v_user_id;78 PERFORM test.assert_equal( v_user_rec.state, 'active');77 SELECT INTO user_rec * FROM users WHERE user_id = user_id; 78 PERFORM test.assert_equal(user_rec.state, 'active'); 79 79 END MAIN; 80 80 … … 92 92 doesn't hold: 93 93 94 * test.assert( p_assertion boolean, p_msg text): this is the 'catch-all'94 * test.assert(assertion boolean, msg text): this is the 'catch-all' 95 95 to assert anything that can be evaluated to a boolean. For example, 96 96 PERFORM test.assert(substring(a from b), b||" not found in "||a); 97 * test.assert_equal( p_1 anyelement, p_2 anyelement)98 * test.assert_not_equal( p_1 anyelement, p_2 anyelement)99 * test.assert_less_than( p_1 anyelement, p_2 anyelement)100 * test.assert_values( p_column text, p_source text, p_expected anyarray):101 Raises an exception if SELECT p_column FROM p_source != p_expected.97 * test.assert_equal(1 anyelement, 2 anyelement) 98 * test.assert_not_equal(1 anyelement, 2 anyelement) 99 * test.assert_less_than(1 anyelement, 2 anyelement) 100 * test.assert_values(column text, source text, expected anyarray): 101 Raises an exception if SELECT column FROM source != expected. 102 102 103 * test.assert_raises( p_call text, p_errm text, p_state text): Raises an104 exception if 'SELECT * FROM [ p_call];' does not raise p_errm.103 * test.assert_raises(call text, errm text, state text): Raises an 104 exception if 'SELECT * FROM [call];' does not raise errm. 105 105 106 106 Some return dynamic SQL: 107 107 108 * test.record_asserter( p_varname1 text, p_varname2 text, p_colnames text):108 * test.record_asserter(varname1 text, varname2 text, colnames text): 109 109 Returns EXECUTE-able SQL to assert equal fields for the two records. 110 110 LOOP over its results and PERFORM each one. … … 184 184 185 185 BEGIN 186 CREATE TABLE test.results (name text PRIMARY KEY, module text, ok boolean, errcode text, errmsg text); 186 CREATE TABLE test.results (name text PRIMARY KEY, module text, 187 ok boolean, errcode text, errmsg text); 187 188 EXCEPTION WHEN duplicate_table THEN 188 189 NULL; … … 190 191 191 192 BEGIN 192 CREATE TYPE test.suite_results AS (name text, module text, result text, errcode text, errmsg text); 193 CREATE TYPE test.suite_results AS (name text, module text, result text, 194 errcode text, errmsg text); 193 195 EXCEPTION WHEN duplicate_table THEN 194 196 NULL; 195 197 END; 196 198 197 199 RETURN TRUE; 198 200 END; … … 203 205 204 206 205 CREATE OR REPLACE FUNCTION test.run_test( p_testname text) RETURNS boolean AS $$207 CREATE OR REPLACE FUNCTION test.run_test(testname text) RETURNS boolean AS $$ 206 208 -- Runs the named test, stores in test.results, and returns success. 207 209 BEGIN 208 DELETE FROM test.results WHERE name = p_testname;209 210 BEGIN 211 EXECUTE 'SELECT * FROM test.' || p_testname || '();';210 DELETE FROM test.results WHERE name = testname; 211 212 BEGIN 213 EXECUTE 'SELECT * FROM test.' || testname || '();'; 212 214 EXCEPTION WHEN OTHERS THEN 213 215 IF SQLERRM = '[OK]' THEN 214 INSERT INTO test.results (name, ok) VALUES ( p_testname, TRUE);216 INSERT INTO test.results (name, ok) VALUES (testname, TRUE); 215 217 RETURN TRUE; 216 218 ELSE 217 219 INSERT INTO test.results (name, ok, errcode, errmsg) 218 VALUES (p_testname, FALSE, SQLSTATE, SQLERRM);220 VALUES (testname, FALSE, SQLSTATE, SQLERRM); 219 221 RETURN FALSE; 220 222 END IF; 221 223 END; 222 RAISE EXCEPTION 'Test % did not raise an exception as it should have. Exceptions must ALWAYS be raised in test procedures for rollback.', p_testname; 224 225 RAISE EXCEPTION 'Test % did not raise an exception as it should have. Exceptions must ALWAYS be raised in test procedures for rollback.', testname; 223 226 END; 224 227 $$ LANGUAGE plpgsql; … … 226 229 227 230 CREATE OR REPLACE VIEW test.testnames AS 228 SELECT pg_proc.proname AS name,231 SELECT pg_proc.proname AS name, 229 232 substring(pg_proc.prosrc from E'--\\s+module[:]\\s+(\\S+)') AS module 230 FROM pg_namespace LEFT JOIN pg_proc231 ON pg_proc.pronamespace::oid = pg_namespace.oid::oid232 WHERE pg_namespace.nspname = 'test'233 FROM pg_namespace LEFT JOIN pg_proc 234 ON pg_proc.pronamespace::oid = pg_namespace.oid::oid 235 WHERE pg_namespace.nspname = 'test' 233 236 AND pg_proc.proname LIKE E'test\_%'; 234 237 235 238 236 CREATE OR REPLACE FUNCTION test.run_module( p_module text) RETURNS SETOF test.suite_results AS $$239 CREATE OR REPLACE FUNCTION test.run_module(modulename text) RETURNS SETOF test.suite_results AS $$ 237 240 -- Runs all tests in the given module, stores in test.results, and returns results. 238 241 DECLARE 239 v_testnamepg_proc.proname%TYPE;240 output_record test.suite_results%ROWTYPE;241 BEGIN 242 FOR v_testname IN SELECT name, module FROM test.testnames WHERE module = p_module242 testname pg_proc.proname%TYPE; 243 output_record test.suite_results%ROWTYPE; 244 BEGIN 245 FOR testname IN SELECT name, module FROM test.testnames WHERE module = modulename 243 246 LOOP 244 PERFORM test.run_test( v_testname);245 UPDATE test.results SET module = p_module WHERE name = v_testname;247 PERFORM test.run_test(testname); 248 UPDATE test.results SET module = modulename WHERE name = testname; 246 249 END LOOP; 247 250 248 251 FOR output_record in 249 SELECT name, module, CASE WHEN ok=true THEN '[OK]' ELSE '[FAIL]' END, errcode, errmsg 252 SELECT name, module, CASE WHEN ok=true THEN '[OK]' ELSE '[FAIL]' END, 253 errcode, errmsg 250 254 FROM test.results 251 WHERE module = p_module255 WHERE module = modulename 252 256 LOOP 253 257 RETURN NEXT output_record; … … 260 264 -- Runs all known test functions, stores in test.results, and returns results. 261 265 DECLARE 262 v_testname pg_proc.proname%TYPE;263 v_module text;266 testname pg_proc.proname%TYPE; 267 modulename text; 264 268 output_record test.suite_results%ROWTYPE; 265 269 BEGIN 266 FOR v_module in SELECT DISTINCT module FROM test.testnames ORDER BY module ASC270 FOR modulename in SELECT DISTINCT module FROM test.testnames ORDER BY module ASC 267 271 LOOP 268 FOR v_testname IN SELECT name FROM test.testnames WHERE module = v_module272 FOR testname IN SELECT name FROM test.testnames WHERE module = modulename 269 273 LOOP 270 PERFORM test.run_test( v_testname);271 UPDATE test.results SET module = v_module WHERE name = v_testname;274 PERFORM test.run_test(testname); 275 UPDATE test.results SET module = modulename WHERE name = testname; 272 276 END LOOP; 273 277 END LOOP; … … 286 290 287 291 288 CREATE OR REPLACE FUNCTION test.assert( p_assertion boolean, p_msg text) RETURNS VOID AS $$289 -- Raises an exception ( p_msg) if p_assertion is false.290 -- 291 -- p_assertion may not be NULL.292 BEGIN 293 IF p_assertion IS NULL THEN292 CREATE OR REPLACE FUNCTION test.assert(assertion boolean, msg text) RETURNS VOID AS $$ 293 -- Raises an exception (msg) if assertion is false. 294 -- 295 -- assertion may not be NULL. 296 BEGIN 297 IF assertion IS NULL THEN 294 298 RAISE EXCEPTION 'Assertion test may not be NULL.'; 295 299 END IF; 296 300 297 IF NOT p_assertion THEN298 RAISE EXCEPTION '%', p_msg;299 END IF; 300 END; 301 $$ LANGUAGE plpgsql; 302 303 304 CREATE OR REPLACE FUNCTION test.assert_equal( p_1 anyelement, p_2 anyelement) RETURNS VOID AS $$305 -- Raises an exception if p_1 is not equal to p_2.301 IF NOT assertion THEN 302 RAISE EXCEPTION '%', msg; 303 END IF; 304 END; 305 $$ LANGUAGE plpgsql; 306 307 308 CREATE OR REPLACE FUNCTION test.assert_equal(elem_1 anyelement, elem_2 anyelement) RETURNS VOID AS $$ 309 -- Raises an exception if elem_1 is not equal to elem_2. 306 310 -- 307 311 -- The two arguments must be of the same type. If they are not, 308 312 -- you will receive "ERROR: invalid input syntax ..." 309 313 BEGIN 310 IF (( p_1 IS NULL AND NOT (p_2 IS NULL)) OR311 ( p_2 IS NULL AND NOT (p_1 IS NULL)) OR312 p_1 != p_2) THEN313 RAISE EXCEPTION '% != %', p_1, p_2;314 END IF; 315 END; 316 $$ LANGUAGE plpgsql; 317 318 319 CREATE OR REPLACE FUNCTION test.assert_not_equal( p_1 anyelement, p_2 anyelement) RETURNS VOID AS $$320 -- Raises an exception if p_1 is equal to p_2314 IF ((elem_1 IS NULL AND NOT (elem_2 IS NULL)) OR 315 (elem_2 IS NULL AND NOT (elem_1 IS NULL)) OR 316 elem_1 != elem_2) THEN 317 RAISE EXCEPTION '% != %', elem_1, elem_2; 318 END IF; 319 END; 320 $$ LANGUAGE plpgsql; 321 322 323 CREATE OR REPLACE FUNCTION test.assert_not_equal(elem_1 anyelement, elem_2 anyelement) RETURNS VOID AS $$ 324 -- Raises an exception if elem_1 is equal to elem_2 321 325 -- 322 326 -- The two arguments must be of the same type. If they are not, 323 327 -- you will receive "ERROR: invalid input syntax ..." 324 328 BEGIN 325 IF (( p_1 IS NULL AND p_2 IS NULL) OR p_1 = p_2) THEN326 RAISE EXCEPTION '% = %', p_1, p_2;327 END IF; 328 END; 329 $$ LANGUAGE plpgsql; 330 331 332 CREATE OR REPLACE FUNCTION test.assert_less_than_or_equal( p_1 anyelement, p_2 anyelement) RETURNS VOID AS $$333 -- Raises an exception if p_1 > p_2329 IF ((elem_1 IS NULL AND elem_2 IS NULL) OR elem_1 = elem_2) THEN 330 RAISE EXCEPTION '% = %', elem_1, elem_2; 331 END IF; 332 END; 333 $$ LANGUAGE plpgsql; 334 335 336 CREATE OR REPLACE FUNCTION test.assert_less_than_or_equal(elem_1 anyelement, elem_2 anyelement) RETURNS VOID AS $$ 337 -- Raises an exception if elem_1 > elem_2 334 338 -- 335 339 -- The two arguments must be of the same type. If they are not, 336 340 -- you will receive "ERROR: invalid input syntax ..." 337 341 BEGIN 338 IF NOT (p_1 <= p_2) THEN 339 RAISE EXCEPTION '% not less than %', p_1, p_2; 340 END IF; 341 END; 342 $$ LANGUAGE plpgsql; 343 344 345 CREATE OR REPLACE FUNCTION test.assert_raises(p_call text, p_errm text, p_state text) RETURNS VOID AS $$ 346 -- Raises an exception if 'SELECT * FROM [p_call];' does not raise p_errm. 342 IF (elem_1 IS NULL or elem_2 IS NULL) THEN 343 RAISE EXCEPTION 'Assertion arguments may not be NULL.'; 344 END IF; 345 IF NOT (elem_1 <= elem_2) THEN 346 RAISE EXCEPTION '% not <= %', elem_1, elem_2; 347 END IF; 348 END; 349 $$ LANGUAGE plpgsql; 350 351 352 CREATE OR REPLACE FUNCTION test.assert_raises(call text, errm text, state text) RETURNS VOID AS $$ 353 -- Raises an exception if 'SELECT * FROM [call];' does not raise errm. 347 354 -- 348 355 -- Example: … … 350 357 -- PERFORM test.assert_raises('get_transaction_by_id("a")', 'Bad argument', NULL); 351 358 -- 352 -- If p_errm or p_state are NULL, that value will not be tested. This allows359 -- If errm or state are NULL, that value will not be tested. This allows 353 360 -- you to test by message alone (since the 5-char SQLSTATE values are cryptic), 354 361 -- or trap a range of errors by SQLSTATE without regard for the exact message. 355 362 -- 356 363 -- If you don't know the message you want to trap, call this function with 357 -- p_errm = '' and p_state = ''. The resultant error will tell you the364 -- errm = '' and state = ''. The resultant error will tell you the 358 365 -- SQLSTATE and SQLERRM that were raised. 359 366 BEGIN 360 367 BEGIN 361 EXECUTE 'SELECT * FROM '|| p_call||';';368 EXECUTE 'SELECT * FROM '||call||';'; 362 369 EXCEPTION 363 370 WHEN OTHERS THEN 364 IF (( p_state IS NOT NULL AND SQLSTATE != p_state) OR365 ( p_errm IS NOT NULL AND SQLERRM != p_errm)) THEN366 RAISE EXCEPTION 'Call: ''%'' raised ''(%) %'' instead of ''(%) %''.', p_call, SQLSTATE, SQLERRM, p_state, p_errm;371 IF ((state IS NOT NULL AND SQLSTATE != state) OR 372 (errm IS NOT NULL AND SQLERRM != errm)) THEN 373 RAISE EXCEPTION 'Call: ''%'' raised ''(%) %'' instead of ''(%) %''.', call, SQLSTATE, SQLERRM, state, errm; 367 374 END IF; 368 375 RETURN; 369 376 END; 370 RAISE EXCEPTION 'Call: ''%'' did not raise an error.', p_call;371 END; 372 $$ LANGUAGE plpgsql; 373 374 CREATE OR REPLACE FUNCTION test.assert_raises( p_call text, p_errm text) RETURNS VOID AS $$375 -- Implicit p_column version of assert_values376 BEGIN 377 PERFORM test.assert_raises(p_call, p_errm, NULL);378 END; 379 $$ LANGUAGE plpgsql; 380 381 CREATE OR REPLACE FUNCTION test.assert_raises( p_call text) RETURNS VOID AS $$382 -- Implicit p_errm, p_column version of assert_values383 BEGIN 384 PERFORM test.assert_raises(p_call, NULL, NULL);385 END; 386 $$ LANGUAGE plpgsql; 387 388 389 390 CREATE OR REPLACE FUNCTION test.record_asserter( p_varname1 text, p_varname2 text, p_colnames text) RETURNS SETOF text AS $$377 RAISE EXCEPTION 'Call: ''%'' did not raise an error.', call; 378 END; 379 $$ LANGUAGE plpgsql; 380 381 CREATE OR REPLACE FUNCTION test.assert_raises(call text, errm text) RETURNS VOID AS $$ 382 -- Implicit column version of assert_values 383 BEGIN 384 PERFORM test.assert_raises(call, errm, NULL); 385 END; 386 $$ LANGUAGE plpgsql; 387 388 CREATE OR REPLACE FUNCTION test.assert_raises(call text) RETURNS VOID AS $$ 389 -- Implicit errm, column version of assert_values 390 BEGIN 391 PERFORM test.assert_raises(call, NULL, NULL); 392 END; 393 $$ LANGUAGE plpgsql; 394 395 396 397 CREATE OR REPLACE FUNCTION test.record_asserter(varname1 text, varname2 text, colnames text) RETURNS SETOF text AS $$ 391 398 -- Returns EXECUTE-able SQL to assert equal fields for the two records. 392 399 -- … … 396 403 -- Example: 397 404 -- 398 -- SELECT INTO v_old * FROM table WHERE id = 1;399 -- SELECT INTO v_new * FROM table WHERE id = 2;405 -- SELECT INTO old * FROM table WHERE id = 1; 406 -- SELECT INTO new * FROM table WHERE id = 2; 400 407 -- FOR assertion in 401 -- SELECT * FROM test.record_asserter(' v_old', 'v_new', 'first, last, city')408 -- SELECT * FROM test.record_asserter('old', 'new', 'first, last, city') 402 409 -- LOOP 403 410 -- PERFORM assertion; … … 405 412 -- 406 413 DECLARE 407 i integer:=1;408 v_colnametext;409 v_colnamestext[];410 BEGIN 411 --TODO: IF p_colnames IS NULL grab colnames from type412 413 v_colnames := string_to_array(p_colnames, ',');414 FOR i IN array_lower( v_colnames, 1)..array_upper(v_colnames, 1)414 i integer:=1; 415 colname text; 416 colnames_arr text[]; 417 BEGIN 418 --TODO: IF colnames IS NULL grab colnames from type 419 420 colnames_arr := string_to_array(colnames, ','); 421 FOR i IN array_lower(colnames_arr, 1)..array_upper(colnames_arr, 1) 415 422 LOOP 416 v_colname := quote_ident(trim(both ' ' from v_colnames[i]));423 colname := quote_ident(trim(both ' ' from colnames_arr[i])); 417 424 RETURN NEXT 'PERFORM test.assert_equal(' || 418 quote_ident( p_varname1) || '.' || v_colname || ', ' ||419 quote_ident( p_varname2) || '.' || v_colname || ');';425 quote_ident(varname1) || '.' || colname || ', ' || 426 quote_ident(varname2) || '.' || colname || ');'; 420 427 END LOOP; 421 428 RETURN; … … 424 431 425 432 426 CREATE OR REPLACE FUNCTION test.assert_values( p_source text, p_expected anyarray, p_columntext) RETURNS VOID AS $$427 -- Raises an exception if SELECT p_column FROM p_source != p_expected.428 -- 429 -- p_column shoudl be the name of the column in p_source to compare.430 -- If NULL, it will be taken from the first column of p_source's output.431 -- 432 -- p_source can be any table, view, or procedure that returns records.433 -- p_expected MUST be an array of the same type as p_column.434 -- Neither p_source nor p_expected need to be sorted.433 CREATE OR REPLACE FUNCTION test.assert_values(source text, expected anyarray, colname text) RETURNS VOID AS $$ 434 -- Raises an exception if SELECT column FROM source != expected. 435 -- 436 -- colname shoudl be the name of the column in source to compare. 437 -- If NULL, it will be taken from the first column of source's output. 438 -- 439 -- source can be any table, view, or procedure that returns records. 440 -- expected MUST be an array of the same type as colname. 441 -- Neither source nor expected need to be sorted. 435 442 -- 436 443 -- Example: 437 444 -- PERFORM test.assert_values('user_id', 438 -- 'get_favorite_user_ids(' || v_user_id || ');',445 -- 'get_favorite_user_ids(' || user_id || ');', 439 446 -- '{24, 10074, 87321}'); 440 447 -- 441 448 DECLARE 442 i integer;443 v_recordrecord;444 v_colnametext;445 BEGIN 446 IF p_columnIS NULL THEN449 i integer; 450 record record; 451 firstname text; 452 BEGIN 453 IF colname IS NULL THEN 447 454 EXECUTE 'CREATE TEMPORARY TABLE _test_assert_values_base AS ' || 448 'SELECT * FROM ' || p_source || ';';449 SELECT INTO v_colname a.attname455 'SELECT * FROM ' || source || ';'; 456 SELECT INTO firstname a.attname 450 457 FROM pg_class c LEFT JOIN pg_attribute a ON c.oid = a.attrelid 451 458 WHERE c.relname = '_test_assert_values_base' … … 454 461 AND a.attnum >= 1 455 462 ORDER BY a.attnum; 456 EXECUTE 'ALTER TABLE _test_assert_values_base RENAME ' || v_colname || ' TO result;';463 EXECUTE 'ALTER TABLE _test_assert_values_base RENAME ' || firstname || ' TO result;'; 457 464 ELSE 458 465 -- Dump the source into a temp table 459 466 EXECUTE 'CREATE TEMPORARY TABLE _test_assert_values_base AS ' || 460 'SELECT ' || p_column || ' AS result FROM ' || p_source || ';';467 'SELECT ' || colname || ' AS result FROM ' || source || ';'; 461 468 END IF; 462 469 … … 465 472 -- doesn't get cached and re-used (or subsequent calls will fail). 466 473 EXECUTE 'CREATE TEMPORARY TABLE _test_assert_values_expected (LIKE _test_assert_values_base);'; 467 FOR i IN array_lower( p_expected, 1)..array_upper(p_expected, 1)474 FOR i IN array_lower(expected, 1)..array_upper(expected, 1) 468 475 LOOP 469 EXECUTE 'INSERT INTO _test_assert_values_expected (result) VALUES (' || quote_literal( p_expected[i]) || ');';476 EXECUTE 'INSERT INTO _test_assert_values_expected (result) VALUES (' || quote_literal(expected[i]) || ');'; 470 477 END LOOP; 471 478 472 479 <<TRY>> 473 480 BEGIN 474 FOR v_record IN EXECUTE '(SELECT * FROM _test_assert_values_base EXCEPT ALL481 FOR record IN EXECUTE '(SELECT * FROM _test_assert_values_base EXCEPT ALL 475 482 SELECT * FROM _test_assert_values_expected)' 476 483 LOOP 477 RAISE EXCEPTION 'result: % not in array: %', v_record.result, p_expected;484 RAISE EXCEPTION 'result: % not in array: %', record.result, expected; 478 485 END LOOP; 479 486 480 FOR v_record IN EXECUTE '(SELECT * FROM _test_assert_values_expected EXCEPT ALL487 FOR record IN EXECUTE '(SELECT * FROM _test_assert_values_expected EXCEPT ALL 481 488 SELECT * FROM _test_assert_values_base)' 482 489 LOOP 483 RAISE EXCEPTION 'element: % not in source: %', v_record.result, p_source;490 RAISE EXCEPTION 'element: % not in source: %', record.result, source; 484 491 END LOOP; 485 492 EXCEPTION WHEN OTHERS THEN … … 495 502 496 503 497 CREATE OR REPLACE FUNCTION test.assert_values( p_source text, p_expected anyarray) RETURNS VOID AS $$498 -- Implicit p_column version of assert_values499 BEGIN 500 PERFORM test.assert_values(p_source, p_expected, NULL);501 END; 502 $$ LANGUAGE plpgsql; 504 CREATE OR REPLACE FUNCTION test.assert_values(source text, expected anyarray) RETURNS VOID AS $$ 505 -- Implicit column version of assert_values 506 BEGIN 507 PERFORM test.assert_values(source, expected, NULL); 508 END; 509 $$ LANGUAGE plpgsql;
