$overrides Field overrides. * @return PR_Core_Peptide_DTO */ function make_peptide_dto( array $overrides = [] ): PR_Core_Peptide_DTO { return new PR_Core_Peptide_DTO( array_merge( [ 'id' => 1, 'title' => 'BPC-157', 'slug' => 'bpc-157', 'content' => '', 'excerpt' => 'A synthetic peptide.', 'status' => 'publish', 'display_name' => 'BPC-157', 'aliases' => [], 'molecular_formula' => '', 'molecular_weight' => 0.0, 'cas_number' => '', 'drugbank_id' => '', 'chembl_id' => '', 'evidence_strength' => 'preclinical', 'editorial_review_status' => 'published', 'last_editorial_review_at' => '', 'medical_editor_id' => 0, 'categories' => [], 'families' => [], ], $overrides ) ); } echo "== JSON-LD v0.6.0 unit tests ==\n\n"; // ── Drug @id ───────────────────────────────────────────────────────────── echo "Drug @id:\n"; $GLOBALS['pr_test_postmeta'][1] = []; $peptide = make_peptide_dto(); $drug_node = ( new PR_Core_Jsonld_Drug() )->build( $peptide ); pr_assert( isset( $drug_node['@id'] ), 'Drug node has @id' ); pr_assert_equals( 'https://peptiderepo.com/peptides/bpc-157/#drug', $drug_node['@id'], 'Drug @id = {permalink}#drug' ); // ── Drug @type ─────────────────────────────────────────────────────────── echo "\nDrug @type:\n"; pr_assert( is_array( $drug_node['@type'] ), 'Drug @type is an array' ); pr_assert( in_array( 'Drug', $drug_node['@type'], true ), '@type includes "Drug"' ); pr_assert( in_array( 'MolecularEntity', $drug_node['@type'], true ), '@type includes "MolecularEntity"' ); // ── Fields present when meta set ───────────────────────────────────────── echo "\nFields present when _pr_* meta set:\n"; $GLOBALS['pr_test_postmeta'][1] = [ '_pr_molecular_formula' => 'C62H98N16O22', '_pr_molecular_weight' => '1419.5', '_pr_aliases' => '["Body Protection Compound-157","PL 14736"]', ]; $drug_node_populated = ( new PR_Core_Jsonld_Drug() )->build( $peptide ); pr_assert( isset( $drug_node_populated['molecularFormula'] ), 'molecularFormula present when meta set' ); pr_assert_equals( 'C62H98N16O22', $drug_node_populated['molecularFormula'], 'molecularFormula value correct' ); pr_assert( isset( $drug_node_populated['molecularWeight'] ), 'molecularWeight present when meta set' ); pr_assert_equals( 'QuantitativeValue', $drug_node_populated['molecularWeight']['@type'], 'molecularWeight @type is QuantitativeValue' ); pr_assert_equals( 1419.5, $drug_node_populated['molecularWeight']['value'], 'molecularWeight value correct' ); pr_assert_equals( 'g/mol', $drug_node_populated['molecularWeight']['unitText'], 'molecularWeight unitText = g/mol' ); pr_assert( isset( $drug_node_populated['alternateName'] ), 'alternateName present when _pr_aliases set' ); pr_assert_equals( 2, count( $drug_node_populated['alternateName'] ), 'alternateName has correct count' ); // ── Fields omitted when meta empty ─────────────────────────────────────── echo "\nFields OMITTED when meta empty:\n"; $GLOBALS['pr_test_postmeta'][1] = []; $drug_node_empty = ( new PR_Core_Jsonld_Drug() )->build( $peptide ); pr_assert( ! isset( $drug_node_empty['molecularFormula'] ), 'molecularFormula OMITTED when meta empty' ); pr_assert( ! isset( $drug_node_empty['molecularWeight'] ), 'molecularWeight OMITTED when meta empty' ); pr_assert( ! isset( $drug_node_empty['alternateName'] ), 'alternateName OMITTED when _pr_aliases empty' ); // ── FAQPage present when _pr_faq_items has items ───────────────────────── echo "\nFAQPage emission:\n"; $faq_builder = new PR_Core_Jsonld_Faq(); $permalink = 'https://peptiderepo.com/peptides/bpc-157/'; $GLOBALS['pr_test_postmeta'][1] = [ '_pr_faq_items' => wp_json_encode( [ [ 'question' => 'What is BPC-157?', 'answer' => 'A synthetic peptide.' ], [ 'question' => 'Is it legal?', 'answer' => 'Research use only.' ], ] ), ]; $faq_piece = $faq_builder->build( 1, $permalink ); pr_assert( null !== $faq_piece, 'FAQPage piece not null when items present' ); pr_assert_equals( 'FAQPage', $faq_piece['@type'], 'FAQPage @type correct' ); pr_assert_equals( $permalink . '#faq', $faq_piece['@id'], 'FAQPage @id = {permalink}#faq' ); pr_assert( isset( $faq_piece['mainEntity'] ), 'FAQPage has mainEntity' ); pr_assert_equals( 2, count( $faq_piece['mainEntity'] ), 'FAQPage has 2 questions' ); pr_assert_equals( 'Question', $faq_piece['mainEntity'][0]['@type'], 'Question @type correct' ); pr_assert( isset( $faq_piece['mainEntity'][0]['acceptedAnswer'] ), 'Question has acceptedAnswer' ); // ── FAQPage absent when _pr_faq_items empty ────────────────────────────── $GLOBALS['pr_test_postmeta'][1] = [ '_pr_faq_items' => '[]' ]; $faq_empty = $faq_builder->build( 1, $permalink ); pr_assert( null === $faq_empty, 'FAQPage piece IS null when _pr_faq_items is "[]"' ); $GLOBALS['pr_test_postmeta'][1] = []; $faq_absent = $faq_builder->build( 1, $permalink ); pr_assert( null === $faq_absent, 'FAQPage piece IS null when _pr_faq_items absent' ); // ── MedicalWebPage retype ──────────────────────────────────────────────── echo "\nMedicalWebPage retype:\n"; $webpage_enricher = new PR_Core_Jsonld_Webpage(); $GLOBALS['pr_test_is_singular'] = true; $GLOBALS['pr_test_singular_type'] = 'peptide'; $result = $webpage_enricher->retype_to_medical_webpage( 'WebPage' ); pr_assert_equals( 'MedicalWebPage', $result, 'returns MedicalWebPage on peptide single' ); $GLOBALS['pr_test_is_singular'] = false; $GLOBALS['pr_test_singular_type'] = ''; $result = $webpage_enricher->retype_to_medical_webpage( 'WebPage' ); pr_assert_equals( 'WebPage', $result, 'passes through unchanged on non-peptide pages' ); // ── No duplicate WebPage / BreadcrumbList nodes ─────────────────────────── echo "\nNo duplicate WebPage/BreadcrumbList nodes:\n"; // Verify our builders only produce Drug and FAQPage — never WebPage or BreadcrumbList. $GLOBALS['pr_test_postmeta'][1] = [ '_pr_faq_items' => wp_json_encode( [ [ 'question' => 'Q', 'answer' => 'A' ] ] ) ]; $types_from_builders = []; foreach ( [ $drug_node_populated, $faq_piece ] as $piece ) { if ( null === $piece ) { continue; } $t = $piece['@type'] ?? ''; foreach ( ( is_array( $t ) ? $t : [ $t ] ) as $type ) { $types_from_builders[] = $type; } } pr_assert( ! in_array( 'WebPage', $types_from_builders, true ), 'WebPage NOT in our emitted pieces' ); pr_assert( ! in_array( 'MedicalWebPage', $types_from_builders, true ), 'MedicalWebPage NOT in injected pieces (Yoast owns node)' ); pr_assert( ! in_array( 'BreadcrumbList', $types_from_builders, true ), 'BreadcrumbList NOT in our emitted pieces' ); pr_assert( in_array( 'Drug', $types_from_builders, true ), 'Drug IS in our emitted pieces' ); pr_assert( in_array( 'FAQPage', $types_from_builders, true ), 'FAQPage IS in our emitted pieces when items present' ); // ── PR_Core_Schema_Sanitizers::sanitize_faq_items() ────────────────────── echo "\nPR_Core_Schema_Sanitizers::sanitize_faq_items():\n"; $valid_input = wp_json_encode( [ [ 'question' => 'What is BPC-157?', 'answer' => 'A peptide.' ], ] ); $result = PR_Core_Schema_Sanitizers::sanitize_faq_items( $valid_input ); $decoded = json_decode( $result, true ); pr_assert( is_array( $decoded ), 'sanitize_faq_items returns JSON-decodable string' ); pr_assert_equals( 1, count( $decoded ), 'correct count after sanitize' ); $result_empty = PR_Core_Schema_Sanitizers::sanitize_faq_items( '[]' ); pr_assert_equals( '[]', $result_empty, 'empty JSON array returns "[]"' ); $result_malformed = PR_Core_Schema_Sanitizers::sanitize_faq_items( 'not-json' ); pr_assert_equals( '[]', $result_malformed, 'malformed JSON returns "[]"' ); // Item missing 'answer' key: should be dropped. $incomplete_input = wp_json_encode( [ [ 'question' => 'No answer here?' ] ] ); $result_incomplete = PR_Core_Schema_Sanitizers::sanitize_faq_items( $incomplete_input ); $decoded_incomplete = json_decode( $result_incomplete, true ); pr_assert_equals( 0, count( $decoded_incomplete ), 'item without answer field dropped' ); // ── Summary ────────────────────────────────────────────────────────────── exit( pr_test_summary() );